Coverage for app / backend / src / tests / test_references.py: 99%

607 statements  

« prev     ^ index     » next       coverage.py v7.13.2, created at 2026-02-03 06:18 +0000

1from datetime import date, datetime, timedelta 

2from unittest.mock import patch 

3 

4import grpc 

5import pytest 

6from google.protobuf import empty_pb2 

7from sqlalchemy import select, 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.utils import create_coordinate, now, to_aware_datetime, today 

28from tests.fixtures.db import generate_user, make_friends, make_user_block 

29from tests.fixtures.misc import PushCollector, email_fields, mock_notification_email 

30from tests.fixtures.sessions import account_session, references_session, requests_session 

31from tests.test_requests import valid_request_text 

32 

33 

34@pytest.fixture(autouse=True) 

35def _(testconfig): 

36 pass 

37 

38 

39def create_host_request( 

40 session: Session, 

41 surfer_user_id: int, 

42 host_user_id: int, 

43 host_request_age: timedelta = timedelta(days=15), 

44 status: HostRequestStatus = HostRequestStatus.confirmed, 

45 host_reason_didnt_meetup: str | None = None, 

46 surfer_reason_didnt_meetup: str | None = None, 

47) -> int: 

48 """ 

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

50 """ 

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

52 to_date = today() - host_request_age 

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

54 conversation = Conversation() 

55 session.add(conversation) 

56 session.flush() 

57 

58 msg1 = Message( 

59 conversation_id=conversation.id, 

60 author_id=surfer_user_id, 

61 message_type=MessageType.chat_created, 

62 ) 

63 msg1.time = fake_created + timedelta(seconds=1) 

64 session.add(msg1) 

65 

66 msg2 = Message( 

67 conversation_id=conversation.id, 

68 author_id=surfer_user_id, 

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

70 message_type=MessageType.text, 

71 ) 

72 msg2.time = fake_created + timedelta(seconds=2) 

73 session.add(msg2) 

74 session.flush() 

75 

76 moderation_state = create_moderation( 

77 session, 

78 ModerationObjectType.HOST_REQUEST, 

79 conversation.id, 

80 surfer_user_id, 

81 ) 

82 

83 host_request = HostRequest( 

84 conversation_id=conversation.id, 

85 surfer_user_id=surfer_user_id, 

86 host_user_id=host_user_id, 

87 from_date=from_date, 

88 to_date=to_date, 

89 status=status, 

90 surfer_last_seen_message_id=msg2.id, 

91 host_reason_didnt_meetup=host_reason_didnt_meetup, 

92 surfer_reason_didnt_meetup=surfer_reason_didnt_meetup, 

93 hosting_city="Test City", 

94 hosting_location=create_coordinate(0, 0), 

95 hosting_radius=10, 

96 moderation_state_id=moderation_state.id, 

97 ) 

98 session.add(host_request) 

99 session.commit() 

100 return host_request.conversation_id 

101 

102 

103def create_host_request_by_date( 

104 session: Session, 

105 surfer_user_id: int, 

106 host_user_id: int, 

107 from_date: date, 

108 to_date: date, 

109 status: HostRequestStatus, 

110 host_sent_request_reminders: int, 

111 last_sent_request_reminder_time: datetime, 

112) -> int: 

113 conversation = Conversation() 

114 session.add(conversation) 

115 session.flush() 

116 

117 msg1 = Message( 

118 conversation_id=conversation.id, 

119 author_id=surfer_user_id, 

120 message_type=MessageType.chat_created, 

121 ) 

122 msg1.time = from_date + timedelta(seconds=1) # type: ignore[assignment] 

123 session.add(msg1) 

124 

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

126 msg2 = Message( 

127 conversation_id=conversation.id, 

128 author_id=surfer_user_id, 

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

130 message_type=MessageType.text, 

131 ) 

132 msg2.time = from_date + timedelta(seconds=2) # type: ignore[assignment] 

133 session.add(msg2) 

134 session.flush() 

135 

136 moderation_state = create_moderation( 

137 session, 

138 ModerationObjectType.HOST_REQUEST, 

139 conversation.id, 

140 surfer_user_id, 

141 ) 

142 

143 host_request = HostRequest( 

144 conversation_id=conversation.id, 

145 surfer_user_id=surfer_user_id, 

146 host_user_id=host_user_id, 

147 from_date=from_date, 

148 to_date=to_date, 

149 status=status, 

150 hosting_city="Test City", 

151 hosting_location=create_coordinate(0, 0), 

152 hosting_radius=10, 

153 moderation_state_id=moderation_state.id, 

154 ) 

155 host_request.host_sent_request_reminders = host_sent_request_reminders 

156 host_request.last_sent_request_reminder_time = last_sent_request_reminder_time 

157 

158 session.add(host_request) 

159 session.commit() 

160 return host_request.conversation_id 

161 

162 

163def create_host_reference( 

164 session: Session, 

165 from_user_id: int, 

166 to_user_id: int, 

167 reference_age: timedelta, 

168 *, 

169 surfing: bool = True, 

170 host_request_id: int | None = None, 

171) -> tuple[int, int]: 

172 if host_request_id: 

173 actual_host_request_id = host_request_id 

174 else: 

175 if surfing: 

176 actual_host_request_id = host_request_id or create_host_request( 

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

178 ) 

179 else: 

180 actual_host_request_id = host_request_id or create_host_request( 

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

182 ) 

183 

184 host_request = session.execute( 

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

186 ).scalar_one() 

187 

188 if host_request.surfer_user_id == from_user_id: 

189 reference_type = ReferenceType.surfed 

190 to_user_id = host_request.host_user_id 

191 assert from_user_id == host_request.surfer_user_id 

192 else: 

193 reference_type = ReferenceType.hosted 

194 to_user_id = host_request.surfer_user_id 

195 assert from_user_id == host_request.host_user_id 

196 

197 reference = Reference( 

198 from_user_id=from_user_id, 

199 to_user_id=to_user_id, 

200 host_request_id=host_request.conversation_id, 

201 text="Dummy reference", 

202 rating=0.5, 

203 was_appropriate=True, 

204 reference_type=reference_type, 

205 ) 

206 reference.time = now() - reference_age 

207 

208 session.add(reference) 

209 session.commit() 

210 return reference.id, actual_host_request_id 

211 

212 

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

214 reference = Reference( 

215 from_user_id=from_user_id, 

216 to_user_id=to_user_id, 

217 reference_type=ReferenceType.friend, 

218 text="Test friend request", 

219 rating=0.4, 

220 was_appropriate=True, 

221 ) 

222 reference.time = now() - reference_age 

223 session.add(reference) 

224 session.commit() 

225 return reference.id 

226 

227 

228def test_ListPagination(db): 

229 user1, token1 = generate_user() 

230 user2, token2 = generate_user() 

231 user3, token3 = generate_user() 

232 user4, token4 = generate_user() 

233 user5, token5 = generate_user() 

234 user6, token6 = generate_user() 

235 user7, token7 = generate_user() 

236 user8, token8 = generate_user() 

237 user9, token9 = generate_user() 

238 

239 with session_scope() as session: 

240 # bidirectional references 

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

242 ref2b, _ = create_host_reference( 

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

244 ) 

245 

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

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

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

249 

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

251 ref5b, _ = create_host_reference( 

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

253 ) 

254 

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

256 

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

258 

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

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

261 

262 # should be visible even under 2 weeks 

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

264 

265 # hidden because it's less than 2 weeks 

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

267 

268 # visible because both were written 

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

270 ref8c, _ = create_host_reference( 

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

272 ) 

273 

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

275 

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

277 

278 with references_session(token2) as api: 

279 # written by user1 

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

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

282 

283 res = api.ListReferences( 

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

285 ) 

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

287 

288 res = api.ListReferences( 

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

290 ) 

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

292 assert not res.next_page_token 

293 

294 # received by user1 

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

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

297 

298 res = api.ListReferences( 

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

300 ) 

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

302 assert not res.next_page_token 

303 

304 # same thing but with filters 

305 res = api.ListReferences( 

306 references_pb2.ListReferencesReq( 

307 to_user_id=user1.id, 

308 reference_type_filter=[ 

309 references_pb2.REFERENCE_TYPE_HOSTED, 

310 references_pb2.REFERENCE_TYPE_SURFED, 

311 references_pb2.REFERENCE_TYPE_FRIEND, 

312 ], 

313 page_size=5, 

314 ) 

315 ) 

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

317 

318 res = api.ListReferences( 

319 references_pb2.ListReferencesReq( 

320 to_user_id=user1.id, 

321 reference_type_filter=[ 

322 references_pb2.REFERENCE_TYPE_HOSTED, 

323 references_pb2.REFERENCE_TYPE_SURFED, 

324 references_pb2.REFERENCE_TYPE_FRIEND, 

325 ], 

326 page_token=res.next_page_token, 

327 page_size=5, 

328 ) 

329 ) 

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

331 assert not res.next_page_token 

332 

333 # received hosting references 

334 res = api.ListReferences( 

335 references_pb2.ListReferencesReq( 

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

337 ) 

338 ) 

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

340 

341 res = api.ListReferences( 

342 references_pb2.ListReferencesReq( 

343 to_user_id=user1.id, 

344 reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED], 

345 page_token=res.next_page_token, 

346 page_size=3, 

347 ) 

348 ) 

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

350 assert not res.next_page_token 

351 

352 # written friend references 

353 res = api.ListReferences( 

354 references_pb2.ListReferencesReq( 

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

356 ) 

357 ) 

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

359 assert not res.next_page_token 

360 

361 # written surfing references 

362 res = api.ListReferences( 

363 references_pb2.ListReferencesReq( 

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

365 ) 

366 ) 

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

368 assert not res.next_page_token 

369 

370 with references_session(token7) as api: 

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

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

373 api.ListReferences( 

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

375 ) 

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

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

378 

379 with references_session(token5) as api: 

380 # from user1 to user2 

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

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

383 assert not res.next_page_token 

384 

385 # from user5 to user1 

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

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

388 assert not res.next_page_token 

389 

390 

391def test_ListReference_banned_deleted_users(db): 

392 user1, token1 = generate_user() 

393 user2, token2 = generate_user() 

394 user3, token3 = generate_user() 

395 

396 with session_scope() as session: 

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

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

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

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

401 

402 with references_session(token1) as api: 

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

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

405 assert len(refs_rec) == 2 

406 assert len(refs_sent) == 2 

407 

408 # ban user2 

409 with session_scope() as session: 

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

411 

412 # reference to and from banned user is hidden 

413 with references_session(token1) as api: 

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

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

416 assert len(refs_rec) == 1 

417 assert len(refs_sent) == 1 

418 

419 # delete user3 

420 with session_scope() as session: 

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

422 

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

424 with references_session(token1) as api: 

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

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

427 assert len(refs_rec) == 1 

428 assert len(refs_sent) == 1 

429 

430 

431def test_WriteFriendReference(db): 

432 user1, token1 = generate_user() 

433 user2, token2 = generate_user() 

434 user3, token3 = generate_user() 

435 

436 # Make user1 and user2 friends 

437 make_friends(user1, user2) 

438 

439 with references_session(token1) as api: 

440 # can write normal friend reference 

441 res = api.WriteFriendReference( 

442 references_pb2.WriteFriendReferenceReq( 

443 to_user_id=user2.id, 

444 text="A test reference", 

445 was_appropriate=True, 

446 rating=0.5, 

447 ) 

448 ) 

449 assert res.from_user_id == user1.id 

450 assert res.to_user_id == user2.id 

451 assert res.reference_type == references_pb2.REFERENCE_TYPE_FRIEND 

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

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

454 assert not res.host_request_id 

455 

456 with references_session(token3) as api: 

457 # check it shows up 

458 res = api.ListReferences( 

459 references_pb2.ListReferencesReq( 

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

461 ) 

462 ) 

463 assert len(res.references) == 1 

464 ref = res.references[0] 

465 assert ref.from_user_id == user1.id 

466 assert ref.to_user_id == user2.id 

467 assert ref.reference_type == references_pb2.REFERENCE_TYPE_FRIEND 

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

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

470 assert not ref.host_request_id 

471 

472 with references_session(token1) as api: 

473 # can't write a second friend reference 

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

475 api.WriteFriendReference( 

476 references_pb2.WriteFriendReferenceReq( 

477 to_user_id=user2.id, 

478 text="A test reference", 

479 was_appropriate=True, 

480 rating=0.5, 

481 ) 

482 ) 

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

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

485 

486 with references_session(token2) as api: 

487 # can't write a reference about yourself 

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

489 api.WriteFriendReference( 

490 references_pb2.WriteFriendReferenceReq( 

491 to_user_id=user2.id, 

492 text="I'm really awesome", 

493 was_appropriate=True, 

494 rating=1.0, 

495 ) 

496 ) 

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

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

499 

500 

501def test_WriteFriendReference_with_empty_text(db): 

502 user1, token1 = generate_user() 

503 user2, token2 = generate_user() 

504 

505 with references_session(token1) as api: 

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

507 api.WriteFriendReference( 

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

509 ) 

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

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

512 

513 

514def test_WriteFriendReference_with_private_text(db, push_collector: PushCollector): 

515 user1, token1 = generate_user() 

516 user2, token2 = generate_user() 

517 

518 # Make users friends 

519 make_friends(user1, user2) 

520 

521 with references_session(token1) as api: 

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

523 with mock_notification_email() as mock2: 523 ↛ anywhereline 523 didn't jump anywhere: it always raised an exception.

524 api.WriteFriendReference( 

525 references_pb2.WriteFriendReferenceReq( 

526 to_user_id=user2.id, 

527 text="They were nice!", 

528 was_appropriate=True, 

529 rating=0.6, 

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

531 ) 

532 ) 

533 

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

535 assert mock1.call_count == 1 

536 assert mock2.call_count == 1 

537 e = email_fields(mock2) 

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

539 assert e.recipient == user2.email 

540 

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

542 assert push.content.title == f"New friend reference from {user1.name}" 

543 assert push.content.body == "They were nice!" 

544 

545 

546def test_WriteFriendReference_requires_friendship(db): 

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

548 user1, token1 = generate_user() 

549 user2, token2 = generate_user() 

550 

551 # Try to write friend reference without being friends 

552 with references_session(token1) as api: 

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

554 api.WriteFriendReference( 

555 references_pb2.WriteFriendReferenceReq( 

556 to_user_id=user2.id, 

557 text="A test reference", 

558 was_appropriate=True, 

559 rating=0.5, 

560 ) 

561 ) 

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

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

564 

565 # Now make them friends 

566 make_friends(user1, user2) 

567 

568 # Should now be able to write a reference 

569 with references_session(token1) as api: 

570 res = api.WriteFriendReference( 

571 references_pb2.WriteFriendReferenceReq( 

572 to_user_id=user2.id, 

573 text="A test reference", 

574 was_appropriate=True, 

575 rating=0.5, 

576 ) 

577 ) 

578 assert res.from_user_id == user1.id 

579 assert res.to_user_id == user2.id 

580 

581 # Test the unfriending scenario: delete the friendship 

582 with session_scope() as session: 

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

584 session.execute( 

585 update(FriendRelationship) 

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

587 .values(status=FriendStatus.cancelled) 

588 ) 

589 

590 # Try to write another friend reference after unfriending 

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

592 with references_session(token2) as api: 

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

594 api.WriteFriendReference( 

595 references_pb2.WriteFriendReferenceReq( 

596 to_user_id=user1.id, 

597 text="Another test reference", 

598 was_appropriate=True, 

599 rating=0.8, 

600 ) 

601 ) 

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

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

604 

605 

606def test_host_request_states_references(db, moderator): 

607 user1, token1 = generate_user() 

608 user2, token2 = generate_user() 

609 

610 with session_scope() as session: 

611 # can't write ref 

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

613 # can write ref 

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

615 # can't write ref 

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

617 # can write ref 

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

619 # can't write ref 

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

621 

622 # Approve host requests so both participants can see them 

623 moderator.approve_host_request(hr1) 

624 moderator.approve_host_request(hr2) 

625 moderator.approve_host_request(hr3) 

626 moderator.approve_host_request(hr4) 

627 moderator.approve_host_request(hr5) 

628 

629 with references_session(token1) as api: 

630 # pending 

631 api.WriteHostRequestReference( 

632 references_pb2.WriteHostRequestReferenceReq( 

633 host_request_id=hr2, 

634 text="Should work!", 

635 was_appropriate=True, 

636 rating=0.9, 

637 ) 

638 ) 

639 

640 # accepted 

641 api.WriteHostRequestReference( 

642 references_pb2.WriteHostRequestReferenceReq( 

643 host_request_id=hr4, 

644 text="Should work!", 

645 was_appropriate=True, 

646 rating=0.9, 

647 ) 

648 ) 

649 

650 # rejected 

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

652 api.WriteHostRequestReference( 

653 references_pb2.WriteHostRequestReferenceReq( 

654 host_request_id=hr1, 

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

656 was_appropriate=True, 

657 rating=0.9, 

658 ) 

659 ) 

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

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

662 

663 # confirmed 

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

665 api.WriteHostRequestReference( 

666 references_pb2.WriteHostRequestReferenceReq( 

667 host_request_id=hr3, 

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

669 was_appropriate=True, 

670 rating=0.9, 

671 ) 

672 ) 

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

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

675 

676 # cancelled 

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

678 api.WriteHostRequestReference( 

679 references_pb2.WriteHostRequestReferenceReq( 

680 host_request_id=hr5, 

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

682 was_appropriate=True, 

683 rating=0.9, 

684 ) 

685 ) 

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

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

688 

689 

690def test_WriteHostRequestReference(db, moderator): 

691 user1, token1 = generate_user() 

692 user2, token2 = generate_user() 

693 user3, token3 = generate_user() 

694 user4, token4 = generate_user() 

695 

696 with session_scope() as session: 

697 # too old 

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

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

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

701 # valid surfing req 

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

703 # not yet complete 

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

705 # we indicated we didn't meet 

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

707 # we will indicate we didn't meet 

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

709 

710 # Approve host requests so both participants can see them 

711 moderator.approve_host_request(hr1) 

712 moderator.approve_host_request(hr2) 

713 moderator.approve_host_request(hr3) 

714 moderator.approve_host_request(hr4) 

715 moderator.approve_host_request(hr5) 

716 moderator.approve_host_request(hr6) 

717 

718 with references_session(token3) as api: 

719 # can write for this one 

720 api.WriteHostRequestReference( 

721 references_pb2.WriteHostRequestReferenceReq( 

722 host_request_id=hr3, 

723 text="Should work!", 

724 was_appropriate=True, 

725 rating=0.9, 

726 ) 

727 ) 

728 

729 with references_session(token1) as api: 

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

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

732 api.WriteHostRequestReference( 

733 references_pb2.WriteHostRequestReferenceReq( 

734 host_request_id=hr4, 

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

736 was_appropriate=True, 

737 rating=0.9, 

738 ) 

739 ) 

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

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

742 

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

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

745 api.WriteHostRequestReference( 

746 references_pb2.WriteHostRequestReferenceReq( 

747 host_request_id=hr1, 

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

749 was_appropriate=True, 

750 rating=0.9, 

751 ) 

752 ) 

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

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

755 

756 # can write for this one 

757 api.WriteHostRequestReference( 

758 references_pb2.WriteHostRequestReferenceReq( 

759 host_request_id=hr2, 

760 text="Should work!", 

761 was_appropriate=True, 

762 rating=0.9, 

763 ) 

764 ) 

765 

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

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

768 api.WriteHostRequestReference( 

769 references_pb2.WriteHostRequestReferenceReq( 

770 host_request_id=hr2, 

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

772 was_appropriate=True, 

773 rating=0.9, 

774 ) 

775 ) 

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

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

778 

779 # can write for this one too 

780 api.WriteHostRequestReference( 

781 references_pb2.WriteHostRequestReferenceReq( 

782 host_request_id=hr3, 

783 text="Should work!", 

784 was_appropriate=True, 

785 rating=0.9, 

786 ) 

787 ) 

788 

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

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

791 api.WriteHostRequestReference( 

792 references_pb2.WriteHostRequestReferenceReq( 

793 host_request_id=hr5, 

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

795 was_appropriate=True, 

796 rating=0.9, 

797 ) 

798 ) 

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

800 assert ( 

801 e.value.details() 

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

803 ) 

804 

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

806 api.HostRequestIndicateDidntMeetup( 

807 references_pb2.HostRequestIndicateDidntMeetupReq( 

808 host_request_id=hr6, 

809 reason_didnt_meetup="No clue?", 

810 ) 

811 ) 

812 

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

814 api.WriteHostRequestReference( 

815 references_pb2.WriteHostRequestReferenceReq( 

816 host_request_id=hr6, 

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

818 was_appropriate=True, 

819 rating=0.9, 

820 ) 

821 ) 

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

823 assert ( 

824 e.value.details() 

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

826 ) 

827 

828 with references_session(token4) as api: 

829 # they can still write one 

830 api.WriteHostRequestReference( 

831 references_pb2.WriteHostRequestReferenceReq( 

832 host_request_id=hr6, 

833 text="Should work!", 

834 was_appropriate=True, 

835 rating=0.9, 

836 ) 

837 ) 

838 

839 

840def test_WriteHostRequestReference_private_text(db, push_collector: PushCollector): 

841 user1, token1 = generate_user() 

842 user2, token2 = generate_user() 

843 

844 with session_scope() as session: 

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

846 

847 with references_session(token1) as api: 

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

849 with mock_notification_email() as mock2: 849 ↛ anywhereline 849 didn't jump anywhere: it always raised an exception.

850 api.WriteHostRequestReference( 

851 references_pb2.WriteHostRequestReferenceReq( 

852 host_request_id=hr, 

853 text="Should work!", 

854 was_appropriate=True, 

855 rating=0.9, 

856 private_text="Something", 

857 ) 

858 ) 

859 

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

861 assert mock1.call_count == 1 

862 assert mock2.call_count == 1 

863 

864 e = email_fields(mock2) 

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

866 assert e.recipient == user2.email 

867 

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

869 assert push.content.title == f"New reference from {user1.name}" 

870 assert push.content.body == f"{user1.name} left you a reference, now it's your turn to write theirs!" 

871 

872 

873def test_GetHostRequestReferenceStatus(db, moderator): 

874 user1, token1 = generate_user() 

875 user2, token2 = generate_user() 

876 

877 # user1 writes; RPC returns has_given True 

878 with session_scope() as session: 

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

880 moderator.approve_host_request(hr1) 

881 with references_session(token1) as api: 

882 api.WriteHostRequestReference( 

883 references_pb2.WriteHostRequestReferenceReq( 

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

885 ) 

886 ) 

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

888 assert res.has_given is True 

889 

890 # false: no reference written yet 

891 with session_scope() as session: 

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

893 moderator.approve_host_request(hr2) 

894 with references_session(token1) as api: 

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

896 assert res.has_given is False 

897 

898 # false: other user wrote a reference 

899 with session_scope() as session: 

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

901 moderator.approve_host_request(hr3) 

902 with references_session(token2) as api: 

903 api.WriteHostRequestReference( 

904 references_pb2.WriteHostRequestReferenceReq( 

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

906 ) 

907 ) 

908 with references_session(token1) as api: 

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

910 assert res.has_given is False 

911 

912 # false: nonexistent host request id 

913 with references_session(token1) as api: 

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

915 assert res.has_given is False 

916 

917 # Additional status flags 

918 with session_scope() as session: 

919 # expired (too old) 

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

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

922 hr_didnt_stay_host = create_host_request( 

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

924 ) 

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

926 hr_other_didnt_stay = create_host_request( 

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

928 ) 

929 

930 moderator.approve_host_request(hr_expired) 

931 moderator.approve_host_request(hr_didnt_stay_host) 

932 moderator.approve_host_request(hr_other_didnt_stay) 

933 

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

935 with references_session(token1) as api: 

936 res = api.GetHostRequestReferenceStatus( 

937 references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr_expired) 

938 ) 

939 assert res.has_given is False 

940 assert res.is_expired is True 

941 assert res.can_write is False 

942 assert res.didnt_stay is False 

943 

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

945 with references_session(token1) as api: 

946 res = api.GetHostRequestReferenceStatus( 

947 references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr_didnt_stay_host) 

948 ) 

949 assert res.has_given is False 

950 assert res.is_expired is False 

951 assert res.didnt_stay is True 

952 assert res.can_write is False 

953 

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

955 with references_session(token1) as api: 

956 res = api.GetHostRequestReferenceStatus( 

957 references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr_other_didnt_stay) 

958 ) 

959 assert res.has_given is False 

960 assert res.is_expired is False 

961 assert res.didnt_stay is False 

962 assert res.can_write is True 

963 

964 

965def test_AvailableWriteReferences_and_ListPendingReferencesToWrite(db, moderator): 

966 user1, token1 = generate_user() 

967 user2, token2 = generate_user() 

968 user3, token3 = generate_user() 

969 user4, token4 = generate_user() 

970 user5, token5 = generate_user(delete_user=True) 

971 user6, token6 = generate_user() 

972 user7, token7 = generate_user() 

973 user8, token8 = generate_user() 

974 user9, token9 = generate_user() 

975 user10, token10 = generate_user() 

976 user11, token11 = generate_user() 

977 make_user_block(user1, user6) 

978 make_user_block(user7, user1) 

979 

980 with session_scope() as session: 

981 # too old 

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

983 

984 # already wrote friend ref to user3 

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

986 

987 # already given 

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

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

990 

991 # valid hosted 

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

993 

994 # valid surfed 

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

996 

997 # not yet complete 

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

999 

1000 # already wrote friend ref to user2 

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

1002 

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

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

1005 

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

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

1008 

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

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

1011 

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

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

1014 

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

1016 hr_user9 = create_host_request( 

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

1018 ) 

1019 

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

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

1022 

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

1024 hr7 = create_host_request( 

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

1026 ) 

1027 

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

1029 moderator.approve_host_request(hr1) 

1030 moderator.approve_host_request(hr2) 

1031 moderator.approve_host_request(hr3) 

1032 moderator.approve_host_request(hr4) 

1033 moderator.approve_host_request(hr5) 

1034 moderator.approve_host_request(hr_user5) 

1035 moderator.approve_host_request(hr_user6) 

1036 moderator.approve_host_request(hr_user7) 

1037 moderator.approve_host_request(hr_user8) 

1038 moderator.approve_host_request(hr_user9) 

1039 moderator.approve_host_request(hr6) 

1040 moderator.approve_host_request(hr7) 

1041 

1042 refresh_materialized_views_rapid(empty_pb2.Empty()) 

1043 

1044 with references_session(token1) as api: 

1045 # can't write reference for invisible user 

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

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

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

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

1050 

1051 # can't write reference for blocking user 

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

1053 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user7.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 blocked user 

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

1059 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user6.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 anything to myself 

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

1065 assert not res.can_write_friend_reference 

1066 assert len(res.available_write_references) == 0 

1067 

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

1069 # can't write friend ref to user2 

1070 assert not res.can_write_friend_reference 

1071 # none we can write for user2 

1072 assert len(res.available_write_references) == 0 

1073 

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

1075 # can't write friend ref to user3 

1076 assert not res.can_write_friend_reference 

1077 # can write one reference because we hosted user3 

1078 assert len(res.available_write_references) == 1 

1079 w = res.available_write_references[0] 

1080 assert w.host_request_id == hr3 

1081 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

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

1083 

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

1085 # can write friend ref to user4 

1086 assert res.can_write_friend_reference 

1087 # can write one reference because we surfed with user4 

1088 assert len(res.available_write_references) == 1 

1089 w = res.available_write_references[0] 

1090 assert w.host_request_id == hr4 

1091 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

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

1093 

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

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

1096 assert len(res.available_write_references) == 0 

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

1098 assert len(res.available_write_references) == 0 

1099 

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

1101 # surfed with them 

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

1103 assert len(res.available_write_references) == 1 

1104 w = res.available_write_references[0] 

1105 assert w.host_request_id == hr6 

1106 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

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

1108 # hosted them 

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

1110 assert len(res.available_write_references) == 1 

1111 w = res.available_write_references[0] 

1112 assert w.host_request_id == hr7 

1113 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

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

1115 

1116 # finally check the general list 

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

1118 assert len(res.pending_references) == 4 

1119 w = res.pending_references[0] 

1120 assert w.host_request_id == hr3 

1121 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

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

1123 w = res.pending_references[1] 

1124 assert w.host_request_id == hr4 

1125 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

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

1127 w = res.pending_references[2] 

1128 assert w.host_request_id == hr6 

1129 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED 

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

1131 w = res.pending_references[3] 

1132 assert w.host_request_id == hr7 

1133 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED 

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

1135 

1136 with account_session(token1) as account: 

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

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

1139 "write_reference_reminder", 

1140 "write_reference_reminder", 

1141 "write_reference_reminder", 

1142 "write_reference_reminder", 

1143 "complete_verification_reminder", 

1144 ] 

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

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

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

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

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

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

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

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

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

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

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

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

1157 

1158 

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

1160def test_regression_disappearing_refs(db, hs, moderator): 

1161 """ 

1162 Roughly the reproduction steps are: 

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

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

1165 * On the surfer account, leave a reference 

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

1167 """ 

1168 user1, token1 = generate_user() 

1169 user2, token2 = generate_user() 

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

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

1172 with requests_session(token1) as api: 

1173 res = api.CreateHostRequest( 

1174 requests_pb2.CreateHostRequestReq( 

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

1176 ) 

1177 ) 

1178 host_request_id = res.host_request_id 

1179 

1180 moderator.approve_host_request(host_request_id) 

1181 

1182 assert ( 

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

1184 .host_requests[0] 

1185 .latest_message.text.text 

1186 == valid_request_text() 

1187 ) 

1188 

1189 with requests_session(token2) as api: 

1190 api.RespondHostRequest( 

1191 requests_pb2.RespondHostRequestReq( 

1192 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED 

1193 ) 

1194 ) 

1195 

1196 with requests_session(token1) as api: 

1197 api.RespondHostRequest( 

1198 requests_pb2.RespondHostRequestReq( 

1199 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED 

1200 ) 

1201 ) 

1202 

1203 refresh_materialized_views_rapid(empty_pb2.Empty()) 

1204 

1205 with references_session(token1) as api: 

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

1207 assert len(res.pending_references) == 0 

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

1209 assert len(res.available_write_references) == 0 

1210 

1211 with references_session(token2) 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=user1.id)) 

1215 assert len(res.available_write_references) == 0 

1216 

1217 # hack the time backwards 

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

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

1220 with session_scope() as session: 

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

1222 assert host_request.conversation_id == host_request_id 

1223 host_request.from_date = hack_req_start 

1224 host_request.to_date = hack_req_end 

1225 

1226 with references_session(token1) as api: 

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

1228 assert len(res.pending_references) == 1 

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

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

1231 

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

1233 assert len(res.available_write_references) == 1 

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

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

1236 

1237 with references_session(token2) as api: 

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

1239 assert len(res.pending_references) == 1 

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

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

1242 

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

1244 assert len(res.available_write_references) == 1 

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

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

1247 

1248 if hs == "host": 

1249 with references_session(token2) as api: 

1250 api.WriteHostRequestReference( 

1251 references_pb2.WriteHostRequestReferenceReq( 

1252 host_request_id=host_request_id, 

1253 text="Good stuff", 

1254 was_appropriate=True, 

1255 rating=0.86, 

1256 ) 

1257 ) 

1258 

1259 with references_session(token2) as api: 

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

1261 assert len(res.pending_references) == 0 

1262 

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

1264 assert len(res.available_write_references) == 0 

1265 

1266 with references_session(token1) as api: 

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

1268 assert len(res.pending_references) == 1 

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

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

1271 

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

1273 assert len(res.available_write_references) == 1 

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

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

1276 else: 

1277 with references_session(token1) as api: 

1278 api.WriteHostRequestReference( 

1279 references_pb2.WriteHostRequestReferenceReq( 

1280 host_request_id=host_request_id, 

1281 text="Good stuff", 

1282 was_appropriate=True, 

1283 rating=0.86, 

1284 ) 

1285 ) 

1286 

1287 with references_session(token1) as api: 

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

1289 assert len(res.pending_references) == 0 

1290 

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

1292 assert len(res.available_write_references) == 0 

1293 

1294 with references_session(token2) as api: 

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

1296 assert len(res.pending_references) == 1 

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

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

1299 

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

1301 assert len(res.available_write_references) == 1 

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

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