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

592 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-12-20 11:53 +0000

1from datetime import timedelta 

2from unittest.mock import patch 

3 

4import grpc 

5import pytest 

6from google.protobuf import empty_pb2 

7from sqlalchemy import update 

8from sqlalchemy.orm import Session 

9 

10from couchers.db import session_scope 

11from couchers.materialized_views import refresh_materialized_views_rapid 

12from couchers.models import ( 

13 Conversation, 

14 FriendRelationship, 

15 FriendStatus, 

16 HostRequest, 

17 HostRequestStatus, 

18 Message, 

19 MessageType, 

20 ModerationObjectType, 

21 Reference, 

22 ReferenceType, 

23 User, 

24) 

25from couchers.moderation.utils import create_moderation 

26from couchers.proto import conversations_pb2, references_pb2, requests_pb2 

27from couchers.sql import couchers_select as select 

28from couchers.utils import create_coordinate, now, to_aware_datetime, today 

29from tests.test_fixtures import ( # noqa 

30 account_session, 

31 db, 

32 email_fields, 

33 generate_user, 

34 make_friends, 

35 make_user_block, 

36 mock_notification_email, 

37 moderator, 

38 push_collector, 

39 references_session, 

40 requests_session, 

41 testconfig, 

42) 

43from tests.test_requests import valid_request_text 

44 

45 

46@pytest.fixture(autouse=True) 

47def _(testconfig): 

48 pass 

49 

50 

51def create_host_request( 

52 session, 

53 surfer_user_id, 

54 host_user_id, 

55 host_request_age=timedelta(days=15), 

56 status=HostRequestStatus.confirmed, 

57 host_reason_didnt_meetup=None, 

58 surfer_reason_didnt_meetup=None, 

59): 

60 """ 

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

62 """ 

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

64 to_date = today() - host_request_age 

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

66 conversation = Conversation() 

67 session.add(conversation) 

68 session.flush() 

69 session.add( 

70 Message( 

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

72 conversation_id=conversation.id, 

73 author_id=surfer_user_id, 

74 message_type=MessageType.chat_created, 

75 ) 

76 ) 

77 message = Message( 

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

79 conversation_id=conversation.id, 

80 author_id=surfer_user_id, 

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

82 message_type=MessageType.text, 

83 ) 

84 session.add(message) 

85 session.flush() 

86 

87 moderation_state = create_moderation( 

88 session, 

89 ModerationObjectType.HOST_REQUEST, 

90 conversation.id, 

91 surfer_user_id, 

92 ) 

93 

94 host_request = HostRequest( 

95 conversation_id=conversation.id, 

96 surfer_user_id=surfer_user_id, 

97 host_user_id=host_user_id, 

98 from_date=from_date, 

99 to_date=to_date, 

100 status=status, 

101 surfer_last_seen_message_id=message.id, 

102 host_reason_didnt_meetup=host_reason_didnt_meetup, 

103 surfer_reason_didnt_meetup=surfer_reason_didnt_meetup, 

104 hosting_city="Test City", 

105 hosting_location=create_coordinate(0, 0), 

106 hosting_radius=10, 

107 moderation_state_id=moderation_state.id, 

108 ) 

109 session.add(host_request) 

110 session.commit() 

111 return host_request.conversation_id 

112 

113 

114def create_host_request_by_date( 

115 session, 

116 surfer_user_id, 

117 host_user_id, 

118 from_date, 

119 to_date, 

120 status, 

121 host_sent_request_reminders, 

122 last_sent_request_reminder_time, 

123): 

124 conversation = Conversation() 

125 session.add(conversation) 

126 session.flush() 

127 

128 session.add( 

129 Message( 

130 time=from_date + timedelta(seconds=1), 

131 conversation_id=conversation.id, 

132 author_id=surfer_user_id, 

133 message_type=MessageType.chat_created, 

134 ) 

135 ) 

136 

137 # Unused for now, but every host request must have a message. 

138 message = Message( 

139 time=from_date + timedelta(seconds=2), 

140 conversation_id=conversation.id, 

141 author_id=surfer_user_id, 

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

143 message_type=MessageType.text, 

144 ) 

145 session.add(message) 

146 session.flush() 

147 

148 moderation_state = create_moderation( 

149 session, 

150 ModerationObjectType.HOST_REQUEST, 

151 conversation.id, 

152 surfer_user_id, 

153 ) 

154 

155 host_request = HostRequest( 

156 conversation_id=conversation.id, 

157 surfer_user_id=surfer_user_id, 

158 host_user_id=host_user_id, 

159 from_date=from_date, 

160 to_date=to_date, 

161 status=status, 

162 host_sent_request_reminders=host_sent_request_reminders, 

163 last_sent_request_reminder_time=last_sent_request_reminder_time, 

164 hosting_city="Test City", 

165 hosting_location=create_coordinate(0, 0), 

166 hosting_radius=10, 

167 moderation_state_id=moderation_state.id, 

168 ) 

169 

170 session.add(host_request) 

171 session.commit() 

172 return host_request.conversation_id 

173 

174 

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

176 if host_request_id: 

177 actual_host_request_id = host_request_id 

178 else: 

179 if surfing: 

180 actual_host_request_id = host_request_id or create_host_request( 

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

182 ) 

183 else: 

184 actual_host_request_id = host_request_id or create_host_request( 

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

186 ) 

187 

188 host_request = session.execute( 

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

190 ).scalar_one() 

191 

192 reference = Reference( 

193 time=now() - reference_age, 

194 from_user_id=from_user_id, 

195 host_request_id=host_request.conversation_id, 

196 text="Dummy reference", 

197 rating=0.5, 

198 was_appropriate=True, 

199 ) 

200 

201 if host_request.surfer_user_id == from_user_id: 

202 reference.reference_type = ReferenceType.surfed 

203 reference.to_user_id = host_request.host_user_id 

204 assert from_user_id == host_request.surfer_user_id 

205 else: 

206 reference.reference_type = ReferenceType.hosted 

207 reference.to_user_id = host_request.surfer_user_id 

208 assert from_user_id == host_request.host_user_id 

209 

210 session.add(reference) 

211 session.commit() 

212 return reference.id, actual_host_request_id 

213 

214 

215def create_friend_reference(session: Session, from_user_id: int, to_user_id: int, reference_age: timedelta) -> int: 

216 reference = Reference( 

217 time=now() - reference_age, 

218 from_user_id=from_user_id, 

219 to_user_id=to_user_id, 

220 reference_type=ReferenceType.friend, 

221 text="Test friend request", 

222 rating=0.4, 

223 was_appropriate=True, 

224 ) 

225 session.add(reference) 

226 session.commit() 

227 return reference.id 

228 

229 

230def test_ListPagination(db): 

231 user1, token1 = generate_user() 

232 user2, token2 = generate_user() 

233 user3, token3 = generate_user() 

234 user4, token4 = generate_user() 

235 user5, token5 = generate_user() 

236 user6, token6 = generate_user() 

237 user7, token7 = generate_user() 

238 user8, token8 = generate_user() 

239 user9, token9 = generate_user() 

240 

241 with session_scope() as session: 

242 # bidirectional references 

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

244 ref2b, _ = create_host_reference( 

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

246 ) 

247 

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

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

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

251 

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

253 ref5b, _ = create_host_reference( 

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

255 ) 

256 

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

258 

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

260 

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

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

263 

264 # should be visible even under 2 weeks 

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

266 

267 # hidden because it's less than 2 weeks 

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

269 

270 # visible because both were written 

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

272 ref8c, _ = create_host_reference( 

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

274 ) 

275 

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

277 

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

279 

280 with references_session(token2) as api: 

281 # written by user1 

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

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

284 

285 res = api.ListReferences( 

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

287 ) 

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

289 

290 res = api.ListReferences( 

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

292 ) 

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

294 assert not res.next_page_token 

295 

296 # received by user1 

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

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

299 

300 res = api.ListReferences( 

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

302 ) 

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

304 assert not res.next_page_token 

305 

306 # same thing but with filters 

307 res = api.ListReferences( 

308 references_pb2.ListReferencesReq( 

309 to_user_id=user1.id, 

310 reference_type_filter=[ 

311 references_pb2.REFERENCE_TYPE_HOSTED, 

312 references_pb2.REFERENCE_TYPE_SURFED, 

313 references_pb2.REFERENCE_TYPE_FRIEND, 

314 ], 

315 page_size=5, 

316 ) 

317 ) 

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

319 

320 res = api.ListReferences( 

321 references_pb2.ListReferencesReq( 

322 to_user_id=user1.id, 

323 reference_type_filter=[ 

324 references_pb2.REFERENCE_TYPE_HOSTED, 

325 references_pb2.REFERENCE_TYPE_SURFED, 

326 references_pb2.REFERENCE_TYPE_FRIEND, 

327 ], 

328 page_token=res.next_page_token, 

329 page_size=5, 

330 ) 

331 ) 

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

333 assert not res.next_page_token 

334 

335 # received hosting references 

336 res = api.ListReferences( 

337 references_pb2.ListReferencesReq( 

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

339 ) 

340 ) 

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

342 

343 res = api.ListReferences( 

344 references_pb2.ListReferencesReq( 

345 to_user_id=user1.id, 

346 reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED], 

347 page_token=res.next_page_token, 

348 page_size=3, 

349 ) 

350 ) 

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

352 assert not res.next_page_token 

353 

354 # written friend references 

355 res = api.ListReferences( 

356 references_pb2.ListReferencesReq( 

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

358 ) 

359 ) 

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

361 assert not res.next_page_token 

362 

363 # written surfing references 

364 res = api.ListReferences( 

365 references_pb2.ListReferencesReq( 

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

367 ) 

368 ) 

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

370 assert not res.next_page_token 

371 

372 with references_session(token7) as api: 

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

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

375 api.ListReferences( 

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

377 ) 

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

379 assert e.value.details() == "You need to specify at least one user." 

380 

381 with references_session(token5) as api: 

382 # from user1 to user2 

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

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

385 assert not res.next_page_token 

386 

387 # from user5 to user1 

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

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

390 assert not res.next_page_token 

391 

392 

393def test_ListReference_banned_deleted_users(db): 

394 user1, token1 = generate_user() 

395 user2, token2 = generate_user() 

396 user3, token3 = generate_user() 

397 

398 with session_scope() as session: 

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

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

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

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

403 

404 with references_session(token1) as api: 

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

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

407 assert len(refs_rec) == 2 

408 assert len(refs_sent) == 2 

409 

410 # ban user2 

411 with session_scope() as session: 

412 session.execute(update(User).where(User.username == user2.username).values(is_banned=True)) 

413 

414 # reference to and from banned user is hidden 

415 with references_session(token1) as api: 

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

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

418 assert len(refs_rec) == 1 

419 assert len(refs_sent) == 1 

420 

421 # delete user3 

422 with session_scope() as session: 

423 session.execute(update(User).where(User.username == user3.username).values(is_deleted=True)) 

424 

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

426 with references_session(token1) as api: 

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

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

429 assert len(refs_rec) == 1 

430 assert len(refs_sent) == 1 

431 

432 

433def test_WriteFriendReference(db): 

434 user1, token1 = generate_user() 

435 user2, token2 = generate_user() 

436 user3, token3 = generate_user() 

437 

438 # Make user1 and user2 friends 

439 make_friends(user1, user2) 

440 

441 with references_session(token1) as api: 

442 # can write normal friend reference 

443 res = api.WriteFriendReference( 

444 references_pb2.WriteFriendReferenceReq( 

445 to_user_id=user2.id, 

446 text="A test reference", 

447 was_appropriate=True, 

448 rating=0.5, 

449 ) 

450 ) 

451 assert res.from_user_id == user1.id 

452 assert res.to_user_id == user2.id 

453 assert res.reference_type == references_pb2.REFERENCE_TYPE_FRIEND 

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

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

456 assert not res.host_request_id 

457 

458 with references_session(token3) as api: 

459 # check it shows up 

460 res = api.ListReferences( 

461 references_pb2.ListReferencesReq( 

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

463 ) 

464 ) 

465 assert len(res.references) == 1 

466 ref = res.references[0] 

467 assert ref.from_user_id == user1.id 

468 assert ref.to_user_id == user2.id 

469 assert ref.reference_type == references_pb2.REFERENCE_TYPE_FRIEND 

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

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

472 assert not ref.host_request_id 

473 

474 with references_session(token1) as api: 

475 # can't write a second friend reference 

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

477 api.WriteFriendReference( 

478 references_pb2.WriteFriendReferenceReq( 

479 to_user_id=user2.id, 

480 text="A test reference", 

481 was_appropriate=True, 

482 rating=0.5, 

483 ) 

484 ) 

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

486 assert e.value.details() == "Reference already given." 

487 

488 with references_session(token2) as api: 

489 # can't write a reference about yourself 

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

491 api.WriteFriendReference( 

492 references_pb2.WriteFriendReferenceReq( 

493 to_user_id=user2.id, 

494 text="I'm really awesome", 

495 was_appropriate=True, 

496 rating=1.0, 

497 ) 

498 ) 

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

500 assert e.value.details() == "You can't refer yourself." 

501 

502 

503def test_WriteFriendReference_with_empty_text(db): 

504 user1, token1 = generate_user() 

505 user2, token2 = generate_user() 

506 

507 with references_session(token1) as api: 

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

509 api.WriteFriendReference( 

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

511 ) 

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

513 assert e.value.details() == "The text of a reference must not be empty" 

514 

515 

516def test_WriteFriendReference_with_private_text(db, push_collector): 

517 user1, token1 = generate_user() 

518 user2, token2 = generate_user() 

519 

520 # Make users friends 

521 make_friends(user1, user2) 

522 

523 with references_session(token1) as api: 

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

525 with mock_notification_email() as mock2: 

526 api.WriteFriendReference( 

527 references_pb2.WriteFriendReferenceReq( 

528 to_user_id=user2.id, 

529 text="They were nice!", 

530 was_appropriate=True, 

531 rating=0.6, 

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

533 ) 

534 ) 

535 

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

537 assert mock1.call_count == 1 

538 assert mock2.call_count == 1 

539 e = email_fields(mock2) 

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

541 assert e.recipient == user2.email 

542 

543 push_collector.assert_user_has_single_matching( 

544 user2.id, 

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

546 body="They were nice!", 

547 ) 

548 

549 

550def test_WriteFriendReference_requires_friendship(db): 

551 """Test that users must be friends to write friend references""" 

552 user1, token1 = generate_user() 

553 user2, token2 = generate_user() 

554 

555 # Try to write friend reference without being friends 

556 with references_session(token1) as api: 

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

558 api.WriteFriendReference( 

559 references_pb2.WriteFriendReferenceReq( 

560 to_user_id=user2.id, 

561 text="A test reference", 

562 was_appropriate=True, 

563 rating=0.5, 

564 ) 

565 ) 

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

567 assert e.value.details() == "You can only write friend references for confirmed friends." 

568 

569 # Now make them friends 

570 make_friends(user1, user2) 

571 

572 # Should now be able to write a reference 

573 with references_session(token1) as api: 

574 res = api.WriteFriendReference( 

575 references_pb2.WriteFriendReferenceReq( 

576 to_user_id=user2.id, 

577 text="A test reference", 

578 was_appropriate=True, 

579 rating=0.5, 

580 ) 

581 ) 

582 assert res.from_user_id == user1.id 

583 assert res.to_user_id == user2.id 

584 

585 # Test the unfriending scenario: delete the friendship 

586 with session_scope() as session: 

587 # Change the friendship status to cancelled (simulating unfriending) 

588 session.execute( 

589 update(FriendRelationship) 

590 .where(FriendRelationship.from_user_id == user1.id, FriendRelationship.to_user_id == user2.id) 

591 .values(status=FriendStatus.cancelled) 

592 ) 

593 

594 # Try to write another friend reference after unfriending 

595 # (Note: This assumes user1 didn't already write a reference, or we test with user2 writing to user1) 

596 with references_session(token2) as api: 

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

598 api.WriteFriendReference( 

599 references_pb2.WriteFriendReferenceReq( 

600 to_user_id=user1.id, 

601 text="Another test reference", 

602 was_appropriate=True, 

603 rating=0.8, 

604 ) 

605 ) 

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

607 assert e.value.details() == "You can only write friend references for confirmed friends." 

608 

609 

610def test_host_request_states_references(db, moderator): 

611 user1, token1 = generate_user() 

612 user2, token2 = generate_user() 

613 

614 with session_scope() as session: 

615 # can't write ref 

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

617 # can write ref 

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

619 # can't write ref 

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

621 # can write ref 

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

623 # can't write ref 

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

625 

626 # Approve host requests so both participants can see them 

627 moderator.approve_host_request(hr1) 

628 moderator.approve_host_request(hr2) 

629 moderator.approve_host_request(hr3) 

630 moderator.approve_host_request(hr4) 

631 moderator.approve_host_request(hr5) 

632 

633 with references_session(token1) as api: 

634 # pending 

635 api.WriteHostRequestReference( 

636 references_pb2.WriteHostRequestReferenceReq( 

637 host_request_id=hr2, 

638 text="Should work!", 

639 was_appropriate=True, 

640 rating=0.9, 

641 ) 

642 ) 

643 

644 # accepted 

645 api.WriteHostRequestReference( 

646 references_pb2.WriteHostRequestReferenceReq( 

647 host_request_id=hr4, 

648 text="Should work!", 

649 was_appropriate=True, 

650 rating=0.9, 

651 ) 

652 ) 

653 

654 # rejected 

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

656 api.WriteHostRequestReference( 

657 references_pb2.WriteHostRequestReferenceReq( 

658 host_request_id=hr1, 

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

660 was_appropriate=True, 

661 rating=0.9, 

662 ) 

663 ) 

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

665 assert e.value.details() == "You can't write a reference for that host request, or it wasn't found." 

666 

667 # confirmed 

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

669 api.WriteHostRequestReference( 

670 references_pb2.WriteHostRequestReferenceReq( 

671 host_request_id=hr3, 

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

673 was_appropriate=True, 

674 rating=0.9, 

675 ) 

676 ) 

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

678 assert e.value.details() == "You can't write a reference for that host request, or it wasn't found." 

679 

680 # cancelled 

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

682 api.WriteHostRequestReference( 

683 references_pb2.WriteHostRequestReferenceReq( 

684 host_request_id=hr5, 

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

686 was_appropriate=True, 

687 rating=0.9, 

688 ) 

689 ) 

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

691 assert e.value.details() == "You can't write a reference for that host request, or it wasn't found." 

692 

693 

694def test_WriteHostRequestReference(db, moderator): 

695 user1, token1 = generate_user() 

696 user2, token2 = generate_user() 

697 user3, token3 = generate_user() 

698 user4, token4 = generate_user() 

699 

700 with session_scope() as session: 

701 # too old 

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

703 # valid host req, surfer said we didn't show up but we can still write a req 

704 hr2 = create_host_request(session, user3.id, user1.id, timedelta(days=10), surfer_reason_didnt_meetup="No show") 

705 # valid surfing req 

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

707 # not yet complete 

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

709 # we indicated we didn't meet 

710 hr5 = create_host_request(session, user4.id, user1.id, timedelta(days=7), host_reason_didnt_meetup="") 

711 # we will indicate we didn't meet 

712 hr6 = create_host_request(session, user4.id, user1.id, timedelta(days=8)) 

713 

714 # Approve host requests so both participants can see them 

715 moderator.approve_host_request(hr1) 

716 moderator.approve_host_request(hr2) 

717 moderator.approve_host_request(hr3) 

718 moderator.approve_host_request(hr4) 

719 moderator.approve_host_request(hr5) 

720 moderator.approve_host_request(hr6) 

721 

722 with references_session(token3) as api: 

723 # can write for this one 

724 api.WriteHostRequestReference( 

725 references_pb2.WriteHostRequestReferenceReq( 

726 host_request_id=hr3, 

727 text="Should work!", 

728 was_appropriate=True, 

729 rating=0.9, 

730 ) 

731 ) 

732 

733 with references_session(token1) as api: 

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

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

736 api.WriteHostRequestReference( 

737 references_pb2.WriteHostRequestReferenceReq( 

738 host_request_id=hr4, 

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

740 was_appropriate=True, 

741 rating=0.9, 

742 ) 

743 ) 

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

745 assert e.value.details() == "You can't write a reference for that host request, or it wasn't found." 

746 

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

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

749 api.WriteHostRequestReference( 

750 references_pb2.WriteHostRequestReferenceReq( 

751 host_request_id=hr1, 

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

753 was_appropriate=True, 

754 rating=0.9, 

755 ) 

756 ) 

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

758 assert e.value.details() == "You can't write a reference for that host request, or it wasn't found." 

759 

760 # can write for this one 

761 api.WriteHostRequestReference( 

762 references_pb2.WriteHostRequestReferenceReq( 

763 host_request_id=hr2, 

764 text="Should work!", 

765 was_appropriate=True, 

766 rating=0.9, 

767 ) 

768 ) 

769 

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

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

772 api.WriteHostRequestReference( 

773 references_pb2.WriteHostRequestReferenceReq( 

774 host_request_id=hr2, 

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

776 was_appropriate=True, 

777 rating=0.9, 

778 ) 

779 ) 

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

781 assert e.value.details() == "Reference already given." 

782 

783 # can write for this one too 

784 api.WriteHostRequestReference( 

785 references_pb2.WriteHostRequestReferenceReq( 

786 host_request_id=hr3, 

787 text="Should work!", 

788 was_appropriate=True, 

789 rating=0.9, 

790 ) 

791 ) 

792 

793 # can't write reference for a HR that we indicated we didn't show up 

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

795 api.WriteHostRequestReference( 

796 references_pb2.WriteHostRequestReferenceReq( 

797 host_request_id=hr5, 

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

799 was_appropriate=True, 

800 rating=0.9, 

801 ) 

802 ) 

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

804 assert ( 

805 e.value.details() 

806 == "You can't write a reference for that host request because you indicated that you didn't meet up." 

807 ) 

808 

809 # can't write reference for a HR that we indicate we didn't show up for 

810 api.HostRequestIndicateDidntMeetup( 

811 references_pb2.HostRequestIndicateDidntMeetupReq( 

812 host_request_id=hr6, 

813 reason_didnt_meetup="No clue?", 

814 ) 

815 ) 

816 

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

818 api.WriteHostRequestReference( 

819 references_pb2.WriteHostRequestReferenceReq( 

820 host_request_id=hr6, 

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

822 was_appropriate=True, 

823 rating=0.9, 

824 ) 

825 ) 

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

827 assert ( 

828 e.value.details() 

829 == "You can't write a reference for that host request because you indicated that you didn't meet up." 

830 ) 

831 

832 with references_session(token4) as api: 

833 # they can still write one 

834 api.WriteHostRequestReference( 

835 references_pb2.WriteHostRequestReferenceReq( 

836 host_request_id=hr6, 

837 text="Should work!", 

838 was_appropriate=True, 

839 rating=0.9, 

840 ) 

841 ) 

842 

843 

844def test_WriteHostRequestReference_private_text(db, push_collector): 

845 user1, token1 = generate_user() 

846 user2, token2 = generate_user() 

847 

848 with session_scope() as session: 

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

850 

851 with references_session(token1) as api: 

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

853 with mock_notification_email() as mock2: 

854 api.WriteHostRequestReference( 

855 references_pb2.WriteHostRequestReferenceReq( 

856 host_request_id=hr, 

857 text="Should work!", 

858 was_appropriate=True, 

859 rating=0.9, 

860 private_text="Something", 

861 ) 

862 ) 

863 

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

865 assert mock1.call_count == 1 

866 assert mock2.call_count == 1 

867 

868 e = email_fields(mock2) 

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

870 assert e.recipient == user2.email 

871 

872 push_collector.assert_user_has_single_matching( 

873 user2.id, 

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

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

876 ) 

877 

878 

879def test_GetHostRequestReferenceStatus(db, moderator): 

880 user1, token1 = generate_user() 

881 user2, token2 = generate_user() 

882 

883 # user1 writes; RPC returns has_given True 

884 with session_scope() as session: 

885 hr1 = create_host_request(session, user1.id, user2.id, timedelta(days=7)) 

886 moderator.approve_host_request(hr1) 

887 with references_session(token1) as api: 

888 api.WriteHostRequestReference( 

889 references_pb2.WriteHostRequestReferenceReq( 

890 host_request_id=hr1, text="Great stay!", was_appropriate=True, rating=0.9 

891 ) 

892 ) 

893 res = api.GetHostRequestReferenceStatus(references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr1)) 

894 assert res.has_given is True 

895 

896 # false: no reference written yet 

897 with session_scope() as session: 

898 hr2 = create_host_request(session, user1.id, user2.id, timedelta(days=7)) 

899 moderator.approve_host_request(hr2) 

900 with references_session(token1) as api: 

901 res = api.GetHostRequestReferenceStatus(references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr2)) 

902 assert res.has_given is False 

903 

904 # false: other user wrote a reference 

905 with session_scope() as session: 

906 hr3 = create_host_request(session, user1.id, user2.id, timedelta(days=7)) 

907 moderator.approve_host_request(hr3) 

908 with references_session(token2) as api: 

909 api.WriteHostRequestReference( 

910 references_pb2.WriteHostRequestReferenceReq( 

911 host_request_id=hr3, text="Lovely guest!", was_appropriate=True, rating=0.95 

912 ) 

913 ) 

914 with references_session(token1) as api: 

915 res = api.GetHostRequestReferenceStatus(references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr3)) 

916 assert res.has_given is False 

917 

918 # false: nonexistent host request id 

919 with references_session(token1) as api: 

920 res = api.GetHostRequestReferenceStatus(references_pb2.GetHostRequestReferenceStatusReq(host_request_id=999999)) 

921 assert res.has_given is False 

922 

923 # Additional status flags 

924 with session_scope() as session: 

925 # expired (too old) 

926 hr_expired = create_host_request(session, user2.id, user1.id, timedelta(days=20)) 

927 # current user (host) indicated didn't meet up 

928 hr_didnt_stay_host = create_host_request( 

929 session, user2.id, user1.id, timedelta(days=10), host_reason_didnt_meetup="" 

930 ) 

931 # other user (surfer) indicated didn't meet up 

932 hr_other_didnt_stay = create_host_request( 

933 session, user2.id, user1.id, timedelta(days=10), surfer_reason_didnt_meetup="No show" 

934 ) 

935 

936 moderator.approve_host_request(hr_expired) 

937 moderator.approve_host_request(hr_didnt_stay_host) 

938 moderator.approve_host_request(hr_other_didnt_stay) 

939 

940 # expired: is_expired true, can_write false, didnt_stay false 

941 with references_session(token1) as api: 

942 res = api.GetHostRequestReferenceStatus( 

943 references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr_expired) 

944 ) 

945 assert res.has_given is False 

946 assert res.is_expired is True 

947 assert res.can_write is False 

948 assert res.didnt_stay is False 

949 

950 # current user indicated didn't meet up: didnt_stay true, can_write false, not expired 

951 with references_session(token1) as api: 

952 res = api.GetHostRequestReferenceStatus( 

953 references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr_didnt_stay_host) 

954 ) 

955 assert res.has_given is False 

956 assert res.is_expired is False 

957 assert res.didnt_stay is True 

958 assert res.can_write is False 

959 

960 # other party indicated didn't meet up: didnt_stay false, can_write true (within window), not expired 

961 with references_session(token1) as api: 

962 res = api.GetHostRequestReferenceStatus( 

963 references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr_other_didnt_stay) 

964 ) 

965 assert res.has_given is False 

966 assert res.is_expired is False 

967 assert res.didnt_stay is False 

968 assert res.can_write is True 

969 

970 

971def test_AvailableWriteReferences_and_ListPendingReferencesToWrite(db, moderator): 

972 user1, token1 = generate_user() 

973 user2, token2 = generate_user() 

974 user3, token3 = generate_user() 

975 user4, token4 = generate_user() 

976 user5, token5 = generate_user(delete_user=True) 

977 user6, token6 = generate_user() 

978 user7, token7 = generate_user() 

979 user8, token8 = generate_user() 

980 user9, token9 = generate_user() 

981 user10, token10 = generate_user() 

982 user11, token11 = generate_user() 

983 make_user_block(user1, user6) 

984 make_user_block(user7, user1) 

985 

986 with session_scope() as session: 

987 # too old 

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

989 

990 # already wrote friend ref to user3 

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

992 

993 # already given 

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

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

996 

997 # valid hosted 

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

999 

1000 # valid surfed 

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

1002 

1003 # not yet complete 

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

1005 

1006 # already wrote friend ref to user2 

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

1008 

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

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

1011 

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

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

1014 

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

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

1017 

1018 # hosted but we indicated we didn't meet up, no reason; should not show up 

1019 hr_user8 = create_host_request(session, user8.id, user1.id, timedelta(days=11), host_reason_didnt_meetup="") 

1020 

1021 # surfed but we indicated we didn't meet up, has reason; should not show up 

1022 hr_user9 = create_host_request( 

1023 session, user1.id, user9.id, timedelta(days=10), surfer_reason_didnt_meetup="They never showed up!" 

1024 ) 

1025 

1026 # surfed but they indicated we didn't meet up, no reason; should show up 

1027 hr6 = create_host_request(session, user1.id, user10.id, timedelta(days=4), host_reason_didnt_meetup="") 

1028 

1029 # hosted but they indicated we didn't meet up, has reason; should show up 

1030 hr7 = create_host_request( 

1031 session, user11.id, user1.id, timedelta(days=3), surfer_reason_didnt_meetup="They never showed up!!" 

1032 ) 

1033 

1034 # Approve all host requests so both participants can see them 

1035 moderator.approve_host_request(hr1) 

1036 moderator.approve_host_request(hr2) 

1037 moderator.approve_host_request(hr3) 

1038 moderator.approve_host_request(hr4) 

1039 moderator.approve_host_request(hr5) 

1040 moderator.approve_host_request(hr_user5) 

1041 moderator.approve_host_request(hr_user6) 

1042 moderator.approve_host_request(hr_user7) 

1043 moderator.approve_host_request(hr_user8) 

1044 moderator.approve_host_request(hr_user9) 

1045 moderator.approve_host_request(hr6) 

1046 moderator.approve_host_request(hr7) 

1047 

1048 refresh_materialized_views_rapid(None) 

1049 

1050 with references_session(token1) as api: 

1051 # can't write reference for invisible user 

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

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

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

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

1056 

1057 # can't write reference for blocking user 

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

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

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

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

1062 

1063 # can't write reference for blocked user 

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

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

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

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

1068 

1069 # can't write anything to myself 

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

1071 assert not res.can_write_friend_reference 

1072 assert len(res.available_write_references) == 0 

1073 

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

1075 # can't write friend ref to user2 

1076 assert not res.can_write_friend_reference 

1077 # none we can write for user2 

1078 assert len(res.available_write_references) == 0 

1079 

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

1081 # can't write friend ref to user3 

1082 assert not res.can_write_friend_reference 

1083 # can write one reference because we hosted user3 

1084 assert len(res.available_write_references) == 1 

1085 w = res.available_write_references[0] 

1086 assert w.host_request_id == hr3 

1087 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

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

1089 

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

1091 # can write friend ref to user4 

1092 assert res.can_write_friend_reference 

1093 # can write one reference because we surfed with user4 

1094 assert len(res.available_write_references) == 1 

1095 w = res.available_write_references[0] 

1096 assert w.host_request_id == hr4 

1097 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

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

1099 

1100 # can't write a req if we indicated we didn't meet up 

1101 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user8.id)) 

1102 assert len(res.available_write_references) == 0 

1103 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user9.id)) 

1104 assert len(res.available_write_references) == 0 

1105 

1106 # can still write ref if the other person indicated we didn't meet up 

1107 # surfed with them 

1108 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user10.id)) 

1109 assert len(res.available_write_references) == 1 

1110 w = res.available_write_references[0] 

1111 assert w.host_request_id == hr6 

1112 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

1113 assert now() + timedelta(days=10) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=11) 

1114 # hosted them 

1115 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user11.id)) 

1116 assert len(res.available_write_references) == 1 

1117 w = res.available_write_references[0] 

1118 assert w.host_request_id == hr7 

1119 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

1120 assert now() + timedelta(days=11) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=12) 

1121 

1122 # finally check the general list 

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

1124 assert len(res.pending_references) == 4 

1125 w = res.pending_references[0] 

1126 assert w.host_request_id == hr3 

1127 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

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

1129 w = res.pending_references[1] 

1130 assert w.host_request_id == hr4 

1131 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

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

1133 w = res.pending_references[2] 

1134 assert w.host_request_id == hr6 

1135 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

1136 assert now() + timedelta(days=10) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=11) 

1137 w = res.pending_references[3] 

1138 assert w.host_request_id == hr7 

1139 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

1140 assert now() + timedelta(days=11) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=12) 

1141 

1142 with account_session(token1) as account: 

1143 reminders = account.GetReminders(empty_pb2.Empty()).reminders 

1144 assert [reminder.WhichOneof("reminder") for reminder in reminders] == [ 

1145 "write_reference_reminder", 

1146 "write_reference_reminder", 

1147 "write_reference_reminder", 

1148 "write_reference_reminder", 

1149 "complete_verification_reminder", 

1150 ] 

1151 assert reminders[0].write_reference_reminder.host_request_id == hr3 

1152 assert reminders[0].write_reference_reminder.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

1153 assert reminders[0].write_reference_reminder.other_user.user_id == user3.id 

1154 assert reminders[1].write_reference_reminder.host_request_id == hr4 

1155 assert reminders[1].write_reference_reminder.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

1156 assert reminders[1].write_reference_reminder.other_user.user_id == user4.id 

1157 assert reminders[2].write_reference_reminder.host_request_id == hr6 

1158 assert reminders[2].write_reference_reminder.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

1159 assert reminders[2].write_reference_reminder.other_user.user_id == user10.id 

1160 assert reminders[3].write_reference_reminder.host_request_id == hr7 

1161 assert reminders[3].write_reference_reminder.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

1162 assert reminders[3].write_reference_reminder.other_user.user_id == user11.id 

1163 

1164 

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

1166def test_regression_disappearing_refs(db, hs, moderator): 

1167 """ 

1168 Roughly the reproduction steps are: 

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

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

1171 * On the surfer account, leave a reference 

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

1173 """ 

1174 user1, token1 = generate_user() 

1175 user2, token2 = generate_user() 

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

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

1178 with requests_session(token1) as api: 

1179 res = api.CreateHostRequest( 

1180 requests_pb2.CreateHostRequestReq( 

1181 host_user_id=user2.id, from_date=req_start, to_date=req_end, text=valid_request_text() 

1182 ) 

1183 ) 

1184 host_request_id = res.host_request_id 

1185 

1186 moderator.approve_host_request(host_request_id) 

1187 

1188 assert ( 

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

1190 .host_requests[0] 

1191 .latest_message.text.text 

1192 == valid_request_text() 

1193 ) 

1194 

1195 with requests_session(token2) as api: 

1196 api.RespondHostRequest( 

1197 requests_pb2.RespondHostRequestReq( 

1198 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED 

1199 ) 

1200 ) 

1201 

1202 with requests_session(token1) as api: 

1203 api.RespondHostRequest( 

1204 requests_pb2.RespondHostRequestReq( 

1205 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED 

1206 ) 

1207 ) 

1208 

1209 refresh_materialized_views_rapid(None) 

1210 

1211 with references_session(token1) as api: 

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

1213 assert len(res.pending_references) == 0 

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

1215 assert len(res.available_write_references) == 0 

1216 

1217 with references_session(token2) as api: 

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

1219 assert len(res.pending_references) == 0 

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

1221 assert len(res.available_write_references) == 0 

1222 

1223 # hack the time backwards 

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

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

1226 with session_scope() as session: 

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

1228 assert host_request.conversation_id == host_request_id 

1229 host_request.from_date = hack_req_start 

1230 host_request.to_date = hack_req_end 

1231 

1232 with references_session(token1) as api: 

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

1234 assert len(res.pending_references) == 1 

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

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

1237 

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

1239 assert len(res.available_write_references) == 1 

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

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

1242 

1243 with references_session(token2) as api: 

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

1245 assert len(res.pending_references) == 1 

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

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

1248 

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

1250 assert len(res.available_write_references) == 1 

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

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

1253 

1254 if hs == "host": 

1255 with references_session(token2) as api: 

1256 api.WriteHostRequestReference( 

1257 references_pb2.WriteHostRequestReferenceReq( 

1258 host_request_id=host_request_id, 

1259 text="Good stuff", 

1260 was_appropriate=True, 

1261 rating=0.86, 

1262 ) 

1263 ) 

1264 

1265 with references_session(token2) as api: 

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

1267 assert len(res.pending_references) == 0 

1268 

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

1270 assert len(res.available_write_references) == 0 

1271 

1272 with references_session(token1) as api: 

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

1274 assert len(res.pending_references) == 1 

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

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

1277 

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

1279 assert len(res.available_write_references) == 1 

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

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

1282 else: 

1283 with references_session(token1) as api: 

1284 api.WriteHostRequestReference( 

1285 references_pb2.WriteHostRequestReferenceReq( 

1286 host_request_id=host_request_id, 

1287 text="Good stuff", 

1288 was_appropriate=True, 

1289 rating=0.86, 

1290 ) 

1291 ) 

1292 

1293 with references_session(token1) as api: 

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

1295 assert len(res.pending_references) == 0 

1296 

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

1298 assert len(res.available_write_references) == 0 

1299 

1300 with references_session(token2) as api: 

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

1302 assert len(res.pending_references) == 1 

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

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

1305 

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

1307 assert len(res.available_write_references) == 1 

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

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