Coverage for src/tests/test_references.py: 100%
463 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-22 06:42 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-22 06:42 +0000
1from datetime import timedelta
2from unittest.mock import patch
4import grpc
5import pytest
6from google.protobuf import empty_pb2
8from couchers import errors
9from couchers.db import session_scope
10from couchers.models import (
11 Conversation,
12 HostRequest,
13 HostRequestStatus,
14 Message,
15 MessageType,
16 Reference,
17 ReferenceType,
18 User,
19)
20from couchers.sql import couchers_select as select
21from couchers.utils import now, to_aware_datetime, today
22from proto import conversations_pb2, references_pb2, requests_pb2
23from tests.test_fixtures import ( # noqa
24 db,
25 email_fields,
26 generate_user,
27 make_user_block,
28 mock_notification_email,
29 push_collector,
30 references_session,
31 requests_session,
32 testconfig,
33)
36@pytest.fixture(autouse=True)
37def _(testconfig):
38 pass
41def create_host_request(
42 session,
43 surfer_user_id,
44 host_user_id,
45 host_request_age=timedelta(days=15),
46 status=HostRequestStatus.confirmed,
47 host_reason_didnt_meetup=None,
48 surfer_reason_didnt_meetup=None,
49):
50 """
51 Create a host request that's `host_request_age` old
52 """
53 from_date = today() - host_request_age - timedelta(days=2)
54 to_date = today() - host_request_age
55 fake_created = now() - host_request_age - timedelta(days=3)
56 conversation = Conversation()
57 session.add(conversation)
58 session.flush()
59 session.add(
60 Message(
61 time=fake_created + timedelta(seconds=1),
62 conversation_id=conversation.id,
63 author_id=surfer_user_id,
64 message_type=MessageType.chat_created,
65 )
66 )
67 message = Message(
68 time=fake_created + timedelta(seconds=2),
69 conversation_id=conversation.id,
70 author_id=surfer_user_id,
71 text="Hi, I'm requesting to be hosted.",
72 message_type=MessageType.text,
73 )
74 session.add(message)
75 session.flush()
76 host_request = HostRequest(
77 conversation_id=conversation.id,
78 surfer_user_id=surfer_user_id,
79 host_user_id=host_user_id,
80 from_date=from_date,
81 to_date=to_date,
82 status=status,
83 surfer_last_seen_message_id=message.id,
84 host_reason_didnt_meetup=host_reason_didnt_meetup,
85 surfer_reason_didnt_meetup=surfer_reason_didnt_meetup,
86 )
87 session.add(host_request)
88 session.commit()
89 return host_request.conversation_id
92def create_host_reference(session, from_user_id, to_user_id, reference_age, *, surfing=True, host_request_id=None):
93 if host_request_id:
94 actual_host_request_id = host_request_id
95 else:
96 if surfing:
97 actual_host_request_id = host_request_id or create_host_request(
98 session, from_user_id, to_user_id, reference_age + timedelta(days=1)
99 )
100 else:
101 actual_host_request_id = host_request_id or create_host_request(
102 session, to_user_id, from_user_id, reference_age + timedelta(days=1)
103 )
105 host_request = session.execute(
106 select(HostRequest).where(HostRequest.conversation_id == actual_host_request_id)
107 ).scalar_one()
109 reference = Reference(
110 time=now() - reference_age,
111 from_user_id=from_user_id,
112 host_request_id=host_request.conversation_id,
113 text="Dummy reference",
114 rating=0.5,
115 was_appropriate=True,
116 )
118 if host_request.surfer_user_id == from_user_id:
119 reference.reference_type = ReferenceType.surfed
120 reference.to_user_id = host_request.host_user_id
121 assert from_user_id == host_request.surfer_user_id
122 else:
123 reference.reference_type = ReferenceType.hosted
124 reference.to_user_id = host_request.surfer_user_id
125 assert from_user_id == host_request.host_user_id
127 session.add(reference)
128 session.commit()
129 return reference.id, actual_host_request_id
132def create_friend_reference(session, from_user_id, to_user_id, reference_age):
133 reference = Reference(
134 time=now() - reference_age,
135 from_user_id=from_user_id,
136 to_user_id=to_user_id,
137 reference_type=ReferenceType.friend,
138 text="Test friend request",
139 rating=0.4,
140 was_appropriate=True,
141 )
142 session.add(reference)
143 session.commit()
144 return reference.id
147def test_ListPagination(db):
148 user1, token1 = generate_user()
149 user2, token2 = generate_user()
150 user3, token3 = generate_user()
151 user4, token4 = generate_user()
152 user5, token5 = generate_user()
153 user6, token6 = generate_user()
154 user7, token7 = generate_user()
155 user8, token8 = generate_user()
156 user9, token9 = generate_user()
158 with session_scope() as session:
159 # bidirectional references
160 ref2, hr2 = create_host_reference(session, user2.id, user1.id, timedelta(days=16, seconds=110), surfing=True)
161 ref2b, _ = create_host_reference(
162 session, user1.id, user2.id, timedelta(days=16, seconds=100), host_request_id=hr2
163 )
165 ref3, _ = create_host_reference(session, user3.id, user1.id, timedelta(days=16, seconds=90), surfing=False)
166 ref4, _ = create_host_reference(session, user4.id, user1.id, timedelta(days=16, seconds=80), surfing=True)
167 ref4b = create_friend_reference(session, user1.id, user4.id, timedelta(days=16, seconds=70))
169 ref5, hr5 = create_host_reference(session, user5.id, user1.id, timedelta(days=16, seconds=60), surfing=False)
170 ref5b, _ = create_host_reference(
171 session, user1.id, user5.id, timedelta(days=16, seconds=50), host_request_id=hr5
172 )
174 ref6, _ = create_host_reference(session, user6.id, user1.id, timedelta(days=16, seconds=40), surfing=True)
176 ref7 = create_friend_reference(session, user7.id, user1.id, timedelta(days=16, seconds=30))
178 ref8, _ = create_host_reference(session, user8.id, user1.id, timedelta(days=16, seconds=20), surfing=False)
179 ref9, _ = create_host_reference(session, user9.id, user1.id, timedelta(days=16, seconds=10), surfing=False)
181 # should be visible even under 2 weeks
182 ref7b = create_friend_reference(session, user1.id, user7.id, timedelta(days=9))
184 # hidden because it's less than 2 weeks
185 ref6hidden, _ = create_host_reference(session, user6.id, user1.id, timedelta(days=5), surfing=False)
187 # visible because both were written
188 ref8b, hr8 = create_host_reference(session, user8.id, user1.id, timedelta(days=3, seconds=20), surfing=False)
189 ref8c, _ = create_host_reference(
190 session, user1.id, user8.id, timedelta(days=3, seconds=10), host_request_id=hr8
191 )
193 # note that visibility tests don't really test real logic
195 # these check the right refs are in the right requests and appear in the right order (latest first)
197 with references_session(token2) as api:
198 # written by user1
199 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id, page_size=2))
200 assert [ref.reference_id for ref in res.references] == [ref8c, ref7b]
202 res = api.ListReferences(
203 references_pb2.ListReferencesReq(from_user_id=user1.id, page_token=res.next_page_token, page_size=2)
204 )
205 assert [ref.reference_id for ref in res.references] == [ref5b, ref4b]
207 res = api.ListReferences(
208 references_pb2.ListReferencesReq(from_user_id=user1.id, page_token=res.next_page_token, page_size=2)
209 )
210 assert [ref.reference_id for ref in res.references] == [ref2b]
211 assert not res.next_page_token
213 # received by user1
214 res = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id, page_size=5))
215 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8, ref7, ref6]
217 res = api.ListReferences(
218 references_pb2.ListReferencesReq(to_user_id=user1.id, page_token=res.next_page_token, page_size=5)
219 )
220 assert [ref.reference_id for ref in res.references] == [ref5, ref4, ref3, ref2]
221 assert not res.next_page_token
223 # same thing but with filters
224 res = api.ListReferences(
225 references_pb2.ListReferencesReq(
226 to_user_id=user1.id,
227 reference_type_filter=[
228 references_pb2.REFERENCE_TYPE_HOSTED,
229 references_pb2.REFERENCE_TYPE_SURFED,
230 references_pb2.REFERENCE_TYPE_FRIEND,
231 ],
232 page_size=5,
233 )
234 )
235 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8, ref7, ref6]
237 res = api.ListReferences(
238 references_pb2.ListReferencesReq(
239 to_user_id=user1.id,
240 reference_type_filter=[
241 references_pb2.REFERENCE_TYPE_HOSTED,
242 references_pb2.REFERENCE_TYPE_SURFED,
243 references_pb2.REFERENCE_TYPE_FRIEND,
244 ],
245 page_token=res.next_page_token,
246 page_size=5,
247 )
248 )
249 assert [ref.reference_id for ref in res.references] == [ref5, ref4, ref3, ref2]
250 assert not res.next_page_token
252 # received hosting references
253 res = api.ListReferences(
254 references_pb2.ListReferencesReq(
255 to_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED], page_size=3
256 )
257 )
258 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8]
260 res = api.ListReferences(
261 references_pb2.ListReferencesReq(
262 to_user_id=user1.id,
263 reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED],
264 page_token=res.next_page_token,
265 page_size=3,
266 )
267 )
268 assert [ref.reference_id for ref in res.references] == [ref5, ref3]
269 assert not res.next_page_token
271 # written friend references
272 res = api.ListReferences(
273 references_pb2.ListReferencesReq(
274 from_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_FRIEND]
275 )
276 )
277 assert [ref.reference_id for ref in res.references] == [ref7b, ref4b]
278 assert not res.next_page_token
280 # written surfing references
281 res = api.ListReferences(
282 references_pb2.ListReferencesReq(
283 from_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_SURFED]
284 )
285 )
286 assert [ref.reference_id for ref in res.references] == [ref8c, ref5b]
287 assert not res.next_page_token
289 with references_session(token7) as api:
290 # need to set at least one of from or to user
291 with pytest.raises(grpc.RpcError) as e:
292 api.ListReferences(
293 references_pb2.ListReferencesReq(reference_type_filter=[references_pb2.REFERENCE_TYPE_SURFED])
294 )
295 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
296 assert e.value.details() == errors.NEED_TO_SPECIFY_AT_LEAST_ONE_USER
298 with references_session(token5) as api:
299 # from user1 to user2
300 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id, to_user_id=user2.id))
301 assert [ref.reference_id for ref in res.references] == [ref2b]
302 assert not res.next_page_token
304 # from user5 to user1
305 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user5.id, to_user_id=user1.id))
306 assert [ref.reference_id for ref in res.references] == [ref5]
307 assert not res.next_page_token
310def test_ListReference_banned_deleted_users(db):
311 user1, token1 = generate_user()
312 user2, token2 = generate_user()
313 user3, token3 = generate_user()
315 with session_scope() as session:
316 create_friend_reference(session, user2.id, user1.id, timedelta(days=15))
317 create_friend_reference(session, user3.id, user1.id, timedelta(days=16))
318 create_friend_reference(session, user1.id, user2.id, timedelta(days=15))
319 create_friend_reference(session, user1.id, user3.id, timedelta(days=16))
321 with references_session(token1) as api:
322 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references
323 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references
324 assert len(refs_rec) == 2
325 assert len(refs_sent) == 2
327 # ban user2
328 with session_scope() as session:
329 user2 = session.execute(select(User).where(User.username == user2.username)).scalar_one()
330 user2.is_banned = True
331 session.commit()
333 # reference to and from banned user is hidden
334 with references_session(token1) as api:
335 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references
336 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references
337 assert len(refs_rec) == 1
338 assert len(refs_sent) == 1
340 # delete user3
341 with session_scope() as session:
342 user3 = session.execute(select(User).where(User.username == user3.username)).scalar_one()
343 user3.is_deleted = True
344 session.commit()
346 # doesn't change; references to and from deleted users remain
347 with references_session(token1) as api:
348 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references
349 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references
350 assert len(refs_rec) == 1
351 assert len(refs_sent) == 1
354def test_WriteFriendReference(db):
355 user1, token1 = generate_user()
356 user2, token2 = generate_user()
357 user3, token3 = generate_user()
359 with references_session(token1) as api:
360 # can write normal friend reference
361 res = api.WriteFriendReference(
362 references_pb2.WriteFriendReferenceReq(
363 to_user_id=user2.id,
364 text="A test reference",
365 was_appropriate=True,
366 rating=0.5,
367 )
368 )
369 assert res.from_user_id == user1.id
370 assert res.to_user_id == user2.id
371 assert res.reference_type == references_pb2.REFERENCE_TYPE_FRIEND
372 assert res.text == "A test reference"
373 assert now() - timedelta(hours=24) <= to_aware_datetime(res.written_time) <= now()
374 assert not res.host_request_id
376 with references_session(token3) as api:
377 # check it shows up
378 res = api.ListReferences(
379 references_pb2.ListReferencesReq(
380 from_user_id=user1.id, to_user_id=user2.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_FRIEND]
381 )
382 )
383 assert len(res.references) == 1
384 ref = res.references[0]
385 assert ref.from_user_id == user1.id
386 assert ref.to_user_id == user2.id
387 assert ref.reference_type == references_pb2.REFERENCE_TYPE_FRIEND
388 assert ref.text == "A test reference"
389 assert now() - timedelta(hours=24) <= to_aware_datetime(ref.written_time) <= now()
390 assert not ref.host_request_id
392 with references_session(token1) as api:
393 # can't write a second friend reference
394 with pytest.raises(grpc.RpcError) as e:
395 api.WriteFriendReference(
396 references_pb2.WriteFriendReferenceReq(
397 to_user_id=user2.id,
398 text="A test reference",
399 was_appropriate=True,
400 rating=0.5,
401 )
402 )
403 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
404 assert e.value.details() == errors.REFERENCE_ALREADY_GIVEN
406 with references_session(token2) as api:
407 # can't write a reference about yourself
408 with pytest.raises(grpc.RpcError) as e:
409 api.WriteFriendReference(
410 references_pb2.WriteFriendReferenceReq(
411 to_user_id=user2.id,
412 text="I'm really awesome",
413 was_appropriate=True,
414 rating=1.0,
415 )
416 )
417 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
418 assert e.value.details() == errors.CANT_REFER_SELF
421def test_WriteFriendReference_with_empty_text(db):
422 user1, token1 = generate_user()
423 user2, token2 = generate_user()
425 with references_session(token1) as api:
426 with pytest.raises(grpc.RpcError) as e:
427 api.WriteFriendReference(
428 references_pb2.WriteFriendReferenceReq(to_user_id=user2.id, text=" ", was_appropriate=True, rating=0.8)
429 )
430 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
431 assert e.value.details() == errors.REFERENCE_NO_TEXT
434def test_WriteFriendReference_with_private_text(db, push_collector):
435 user1, token1 = generate_user()
436 user2, token2 = generate_user()
438 with references_session(token1) as api:
439 with patch("couchers.email.queue_email") as mock1:
440 with mock_notification_email() as mock2:
441 api.WriteFriendReference(
442 references_pb2.WriteFriendReferenceReq(
443 to_user_id=user2.id,
444 text="They were nice!",
445 was_appropriate=True,
446 rating=0.6,
447 private_text="A bit of an odd ball, but a nice person nonetheless.",
448 )
449 )
451 # make sure an email was sent to the user receiving the ref as well as the mods
452 assert mock1.call_count == 1
453 assert mock2.call_count == 1
454 e = email_fields(mock2)
455 assert e.subject == f"[TEST] You've received a friend reference from {user1.name}!"
456 assert e.recipient == user2.email
458 push_collector.assert_user_has_single_matching(
459 user2.id,
460 title=f"You've received a friend reference from {user1.name}!",
461 body="They were nice!",
462 )
465def test_host_request_states_references(db):
466 user1, token1 = generate_user()
467 user2, token2 = generate_user()
469 with session_scope() as session:
470 # can't write ref
471 hr1 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.pending)
472 # can write ref
473 hr2 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.accepted)
474 # can't write ref
475 hr3 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.rejected)
476 # can write ref
477 hr4 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.confirmed)
478 # can't write ref
479 hr5 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.cancelled)
481 with references_session(token1) as api:
482 # pending
483 api.WriteHostRequestReference(
484 references_pb2.WriteHostRequestReferenceReq(
485 host_request_id=hr2,
486 text="Should work!",
487 was_appropriate=True,
488 rating=0.9,
489 )
490 )
492 # accepted
493 api.WriteHostRequestReference(
494 references_pb2.WriteHostRequestReferenceReq(
495 host_request_id=hr4,
496 text="Should work!",
497 was_appropriate=True,
498 rating=0.9,
499 )
500 )
502 # rejected
503 with pytest.raises(grpc.RpcError) as e:
504 api.WriteHostRequestReference(
505 references_pb2.WriteHostRequestReferenceReq(
506 host_request_id=hr1,
507 text="Shouldn't work...",
508 was_appropriate=True,
509 rating=0.9,
510 )
511 )
512 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
513 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST
515 # confirmed
516 with pytest.raises(grpc.RpcError) as e:
517 api.WriteHostRequestReference(
518 references_pb2.WriteHostRequestReferenceReq(
519 host_request_id=hr3,
520 text="Shouldn't work...",
521 was_appropriate=True,
522 rating=0.9,
523 )
524 )
525 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
526 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST
528 # cancelled
529 with pytest.raises(grpc.RpcError) as e:
530 api.WriteHostRequestReference(
531 references_pb2.WriteHostRequestReferenceReq(
532 host_request_id=hr5,
533 text="Shouldn't work...",
534 was_appropriate=True,
535 rating=0.9,
536 )
537 )
538 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
539 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST
542def test_WriteHostRequestReference(db):
543 user1, token1 = generate_user()
544 user2, token2 = generate_user()
545 user3, token3 = generate_user()
546 user4, token4 = generate_user()
548 with session_scope() as session:
549 # too old
550 hr1 = create_host_request(session, user3.id, user1.id, timedelta(days=20))
551 # valid host req, surfer said we didn't show up but we can still write a req
552 hr2 = create_host_request(session, user3.id, user1.id, timedelta(days=10), surfer_reason_didnt_meetup="No show")
553 # valid surfing req
554 hr3 = create_host_request(session, user1.id, user3.id, timedelta(days=7))
555 # not yet complete
556 hr4 = create_host_request(session, user2.id, user1.id, timedelta(days=1), status=HostRequestStatus.pending)
557 # we indicated we didn't meet
558 hr5 = create_host_request(session, user4.id, user1.id, timedelta(days=7), host_reason_didnt_meetup="")
559 # we will indicate we didn't meet
560 hr6 = create_host_request(session, user4.id, user1.id, timedelta(days=8))
562 with references_session(token3) as api:
563 # can write for this one
564 api.WriteHostRequestReference(
565 references_pb2.WriteHostRequestReferenceReq(
566 host_request_id=hr3,
567 text="Should work!",
568 was_appropriate=True,
569 rating=0.9,
570 )
571 )
573 with references_session(token1) as api:
574 # can't write reference for a HR that's not yet finished
575 with pytest.raises(grpc.RpcError) as e:
576 api.WriteHostRequestReference(
577 references_pb2.WriteHostRequestReferenceReq(
578 host_request_id=hr4,
579 text="Shouldn't work...",
580 was_appropriate=True,
581 rating=0.9,
582 )
583 )
584 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
585 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST
587 # can't write reference that's more than 2 weeks old
588 with pytest.raises(grpc.RpcError) as e:
589 api.WriteHostRequestReference(
590 references_pb2.WriteHostRequestReferenceReq(
591 host_request_id=hr1,
592 text="Shouldn't work...",
593 was_appropriate=True,
594 rating=0.9,
595 )
596 )
597 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
598 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST
600 # can write for this one
601 api.WriteHostRequestReference(
602 references_pb2.WriteHostRequestReferenceReq(
603 host_request_id=hr2,
604 text="Should work!",
605 was_appropriate=True,
606 rating=0.9,
607 )
608 )
610 # but can't write a second one for the same one
611 with pytest.raises(grpc.RpcError) as e:
612 api.WriteHostRequestReference(
613 references_pb2.WriteHostRequestReferenceReq(
614 host_request_id=hr2,
615 text="Shouldn't work...",
616 was_appropriate=True,
617 rating=0.9,
618 )
619 )
620 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
621 assert e.value.details() == errors.REFERENCE_ALREADY_GIVEN
623 # can write for this one too
624 api.WriteHostRequestReference(
625 references_pb2.WriteHostRequestReferenceReq(
626 host_request_id=hr3,
627 text="Should work!",
628 was_appropriate=True,
629 rating=0.9,
630 )
631 )
633 # can't write reference for a HR that we indicated we didn't show up
634 with pytest.raises(grpc.RpcError) as e:
635 api.WriteHostRequestReference(
636 references_pb2.WriteHostRequestReferenceReq(
637 host_request_id=hr5,
638 text="Shouldn't work...",
639 was_appropriate=True,
640 rating=0.9,
641 )
642 )
643 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
644 assert e.value.details() == errors.CANT_WRITE_REFERENCE_INDICATED_DIDNT_MEETUP
646 # can't write reference for a HR that we indicate we didn't show up for
647 api.HostRequestIndicateDidntMeetup(
648 references_pb2.HostRequestIndicateDidntMeetupReq(
649 host_request_id=hr6,
650 reason_didnt_meetup="No clue?",
651 )
652 )
654 with pytest.raises(grpc.RpcError) as e:
655 api.WriteHostRequestReference(
656 references_pb2.WriteHostRequestReferenceReq(
657 host_request_id=hr6,
658 text="Shouldn't work...",
659 was_appropriate=True,
660 rating=0.9,
661 )
662 )
663 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
664 assert e.value.details() == errors.CANT_WRITE_REFERENCE_INDICATED_DIDNT_MEETUP
666 with references_session(token4) as api:
667 # they can still write one
668 api.WriteHostRequestReference(
669 references_pb2.WriteHostRequestReferenceReq(
670 host_request_id=hr6,
671 text="Should work!",
672 was_appropriate=True,
673 rating=0.9,
674 )
675 )
678def test_WriteHostRequestReference_private_text(db, push_collector):
679 user1, token1 = generate_user()
680 user2, token2 = generate_user()
682 with session_scope() as session:
683 hr = create_host_request(session, user1.id, user2.id, timedelta(days=10))
685 with references_session(token1) as api:
686 with patch("couchers.email.queue_email") as mock1:
687 with mock_notification_email() as mock2:
688 api.WriteHostRequestReference(
689 references_pb2.WriteHostRequestReferenceReq(
690 host_request_id=hr,
691 text="Should work!",
692 was_appropriate=True,
693 rating=0.9,
694 private_text="Something",
695 )
696 )
698 # make sure an email was sent to the user receiving the ref as well as the mods
699 assert mock1.call_count == 1
700 assert mock2.call_count == 1
702 e = email_fields(mock2)
703 assert e.subject == f"[TEST] You've received a reference from {user1.name}!"
704 assert e.recipient == user2.email
706 push_collector.assert_user_has_single_matching(
707 user2.id,
708 title=f"You've received a reference from {user1.name}!",
709 body="Please go and write a reference for them too. It's a nice gesture and helps us build a community together!",
710 )
713def test_AvailableWriteReferences_and_ListPendingReferencesToWrite(db):
714 user1, token1 = generate_user()
715 user2, token2 = generate_user()
716 user3, token3 = generate_user()
717 user4, token4 = generate_user()
718 user5, token5 = generate_user(delete_user=True)
719 user6, token6 = generate_user()
720 user7, token7 = generate_user()
721 user8, token8 = generate_user()
722 user9, token9 = generate_user()
723 user10, token10 = generate_user()
724 user11, token11 = generate_user()
725 make_user_block(user1, user6)
726 make_user_block(user7, user1)
728 with session_scope() as session:
729 # too old
730 hr1 = create_host_request(session, user3.id, user1.id, timedelta(days=20))
732 # already wrote friend ref to user3
733 create_friend_reference(session, user1.id, user3.id, timedelta(days=15, seconds=70))
735 # already given
736 _, hr2 = create_host_reference(session, user2.id, user1.id, timedelta(days=10, seconds=110), surfing=True)
737 create_host_reference(session, user1.id, user2.id, timedelta(days=10, seconds=100), host_request_id=hr2)
739 # valid hosted
740 hr3 = create_host_request(session, user3.id, user1.id, timedelta(days=8))
742 # valid surfed
743 hr4 = create_host_request(session, user1.id, user4.id, timedelta(days=5))
745 # not yet complete
746 hr5 = create_host_request(session, user2.id, user1.id, timedelta(days=2), status=HostRequestStatus.pending)
748 # already wrote friend ref to user2
749 create_friend_reference(session, user1.id, user2.id, timedelta(days=1))
751 # user5 deleted, reference won't show up as pending
752 create_host_request(session, user1.id, user5.id, timedelta(days=5))
754 # user6 blocked, reference won't show up as pending
755 create_host_request(session, user1.id, user6.id, timedelta(days=5))
757 # user7 blocking, reference won't show up as pending
758 create_host_request(session, user1.id, user7.id, timedelta(days=5))
760 # hosted but we indicated we didn't meet up, no reason; should not show up
761 create_host_request(session, user8.id, user1.id, timedelta(days=11), host_reason_didnt_meetup="")
763 # surfed but we indicated we didn't meet up, has reason; should not show up
764 create_host_request(
765 session, user1.id, user9.id, timedelta(days=10), surfer_reason_didnt_meetup="They never showed up!"
766 )
768 # surfed but they indicated we didn't meet up, no reason; should show up
769 hr6 = create_host_request(session, user1.id, user10.id, timedelta(days=4), host_reason_didnt_meetup="")
771 # hosted but they indicated we didn't meet up, has reason; should show up
772 hr7 = create_host_request(
773 session, user11.id, user1.id, timedelta(days=3), surfer_reason_didnt_meetup="They never showed up!!"
774 )
776 with references_session(token1) as api:
777 # can't write reference for invisible user
778 with pytest.raises(grpc.RpcError) as e:
779 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user5.id))
780 assert e.value.code() == grpc.StatusCode.NOT_FOUND
781 assert e.value.details() == errors.USER_NOT_FOUND
783 # can't write reference for blocking user
784 with pytest.raises(grpc.RpcError) as e:
785 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user7.id))
786 assert e.value.code() == grpc.StatusCode.NOT_FOUND
787 assert e.value.details() == errors.USER_NOT_FOUND
789 # can't write reference for blocked user
790 with pytest.raises(grpc.RpcError) as e:
791 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user6.id))
792 assert e.value.code() == grpc.StatusCode.NOT_FOUND
793 assert e.value.details() == errors.USER_NOT_FOUND
795 # can't write anything to myself
796 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
797 assert not res.can_write_friend_reference
798 assert len(res.available_write_references) == 0
800 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
801 # can't write friend ref to user2
802 assert not res.can_write_friend_reference
803 # none we can write for user2
804 assert len(res.available_write_references) == 0
806 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user3.id))
807 # can't write friend ref to user3
808 assert not res.can_write_friend_reference
809 # can write one reference because we hosted user3
810 assert len(res.available_write_references) == 1
811 w = res.available_write_references[0]
812 assert w.host_request_id == hr3
813 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
814 assert now() + timedelta(days=6) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=7)
816 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user4.id))
817 # can write friend ref to user4
818 assert res.can_write_friend_reference
819 # can write one reference because we surfed with user4
820 assert len(res.available_write_references) == 1
821 w = res.available_write_references[0]
822 assert w.host_request_id == hr4
823 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
824 assert now() + timedelta(days=9) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=10)
826 # can't write a req if we indicated we didn't meet up
827 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user8.id))
828 assert len(res.available_write_references) == 0
829 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user9.id))
830 assert len(res.available_write_references) == 0
832 # can still write ref if the other person indicated we didn't meet up
833 # surfed with them
834 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user10.id))
835 assert len(res.available_write_references) == 1
836 w = res.available_write_references[0]
837 assert w.host_request_id == hr6
838 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
839 assert now() + timedelta(days=10) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=11)
840 # hosted them
841 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user11.id))
842 assert len(res.available_write_references) == 1
843 w = res.available_write_references[0]
844 assert w.host_request_id == hr7
845 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
846 assert now() + timedelta(days=11) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=12)
848 # finally check the general list
849 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
850 assert len(res.pending_references) == 4
851 w = res.pending_references[0]
852 assert w.host_request_id == hr3
853 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
854 assert now() + timedelta(days=6) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=7)
855 w = res.pending_references[1]
856 assert w.host_request_id == hr4
857 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
858 assert now() + timedelta(days=9) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=10)
859 w = res.pending_references[2]
860 assert w.host_request_id == hr6
861 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
862 assert now() + timedelta(days=10) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=11)
863 w = res.pending_references[3]
864 assert w.host_request_id == hr7
865 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
866 assert now() + timedelta(days=11) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=12)
869@pytest.mark.parametrize("hs", ["host", "surfer"])
870def test_regression_disappearing_refs(db, hs):
871 """
872 Roughly the reproduction steps are:
873 * Send a host request, then have both host and surfer accept
874 * Wait for it to elapse (or hack it with SQL like what you told me to do)
875 * On the surfer account, leave a reference
876 * Then on the host account, the option to leave a reference is then not available
877 """
878 user1, token1 = generate_user()
879 user2, token2 = generate_user()
880 req_start = (today() + timedelta(days=2)).isoformat()
881 req_end = (today() + timedelta(days=3)).isoformat()
882 with requests_session(token1) as api:
883 res = api.CreateHostRequest(
884 requests_pb2.CreateHostRequestReq(
885 host_user_id=user2.id, from_date=req_start, to_date=req_end, text="Test request"
886 )
887 )
888 host_request_id = res.host_request_id
889 assert (
890 api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True))
891 .host_requests[0]
892 .latest_message.text.text
893 == "Test request"
894 )
896 with requests_session(token2) as api:
897 api.RespondHostRequest(
898 requests_pb2.RespondHostRequestReq(
899 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
900 )
901 )
903 with requests_session(token1) as api:
904 api.RespondHostRequest(
905 requests_pb2.RespondHostRequestReq(
906 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
907 )
908 )
910 with references_session(token1) as api:
911 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
912 assert len(res.pending_references) == 0
913 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
914 assert len(res.available_write_references) == 0
916 with references_session(token2) as api:
917 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
918 assert len(res.pending_references) == 0
919 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
920 assert len(res.available_write_references) == 0
922 # hack the time backwards
923 hack_req_start = today() - timedelta(days=10) + timedelta(days=2)
924 hack_req_end = today() - timedelta(days=10) + timedelta(days=3)
925 with session_scope() as session:
926 host_request = session.execute(select(HostRequest)).scalar_one()
927 assert host_request.conversation_id == host_request_id
928 host_request.from_date = hack_req_start
929 host_request.to_date = hack_req_end
931 with references_session(token1) as api:
932 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
933 assert len(res.pending_references) == 1
934 assert res.pending_references[0].host_request_id == host_request_id
935 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
937 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
938 assert len(res.available_write_references) == 1
939 assert res.available_write_references[0].host_request_id == host_request_id
940 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
942 with references_session(token2) as api:
943 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
944 assert len(res.pending_references) == 1
945 assert res.pending_references[0].host_request_id == host_request_id
946 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED
948 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
949 assert len(res.available_write_references) == 1
950 assert res.available_write_references[0].host_request_id == host_request_id
951 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED
953 if hs == "host":
954 with references_session(token2) as api:
955 api.WriteHostRequestReference(
956 references_pb2.WriteHostRequestReferenceReq(
957 host_request_id=host_request_id,
958 text="Good stuff",
959 was_appropriate=True,
960 rating=0.86,
961 )
962 )
964 with references_session(token2) as api:
965 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
966 assert len(res.pending_references) == 0
968 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
969 assert len(res.available_write_references) == 0
971 with references_session(token1) as api:
972 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
973 assert len(res.pending_references) == 1
974 assert res.pending_references[0].host_request_id == host_request_id
975 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
977 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
978 assert len(res.available_write_references) == 1
979 assert res.available_write_references[0].host_request_id == host_request_id
980 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
981 else:
982 with references_session(token1) as api:
983 api.WriteHostRequestReference(
984 references_pb2.WriteHostRequestReferenceReq(
985 host_request_id=host_request_id,
986 text="Good stuff",
987 was_appropriate=True,
988 rating=0.86,
989 )
990 )
992 with references_session(token1) as api:
993 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
994 assert len(res.pending_references) == 0
996 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
997 assert len(res.available_write_references) == 0
999 with references_session(token2) as api:
1000 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1001 assert len(res.pending_references) == 1
1002 assert res.pending_references[0].host_request_id == host_request_id
1003 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1005 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
1006 assert len(res.available_write_references) == 1
1007 assert res.available_write_references[0].host_request_id == host_request_id
1008 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED