Coverage for src/tests/test_references.py: 100%
473 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-07-09 00:05 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-07-09 00:05 +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_request_by_date(
93 session,
94 surfer_user_id,
95 host_user_id,
96 from_date,
97 to_date,
98 status,
99 host_sent_request_reminders,
100 last_sent_request_reminder_time,
101):
102 conversation = Conversation()
103 session.add(conversation)
104 session.flush()
106 session.add(
107 Message(
108 time=from_date + timedelta(seconds=1),
109 conversation_id=conversation.id,
110 author_id=surfer_user_id,
111 message_type=MessageType.chat_created,
112 )
113 )
115 message = Message(
116 time=from_date + timedelta(seconds=2),
117 conversation_id=conversation.id,
118 author_id=surfer_user_id,
119 text="Hi, I'm requesting to be hosted.",
120 message_type=MessageType.text,
121 )
123 host_request = HostRequest(
124 conversation_id=conversation.id,
125 surfer_user_id=surfer_user_id,
126 host_user_id=host_user_id,
127 from_date=from_date,
128 to_date=to_date,
129 status=status,
130 host_sent_request_reminders=host_sent_request_reminders,
131 last_sent_request_reminder_time=last_sent_request_reminder_time,
132 )
134 session.add(host_request)
135 session.commit()
136 return host_request.conversation_id
139def create_host_reference(session, from_user_id, to_user_id, reference_age, *, surfing=True, host_request_id=None):
140 if host_request_id:
141 actual_host_request_id = host_request_id
142 else:
143 if surfing:
144 actual_host_request_id = host_request_id or create_host_request(
145 session, from_user_id, to_user_id, reference_age + timedelta(days=1)
146 )
147 else:
148 actual_host_request_id = host_request_id or create_host_request(
149 session, to_user_id, from_user_id, reference_age + timedelta(days=1)
150 )
152 host_request = session.execute(
153 select(HostRequest).where(HostRequest.conversation_id == actual_host_request_id)
154 ).scalar_one()
156 reference = Reference(
157 time=now() - reference_age,
158 from_user_id=from_user_id,
159 host_request_id=host_request.conversation_id,
160 text="Dummy reference",
161 rating=0.5,
162 was_appropriate=True,
163 )
165 if host_request.surfer_user_id == from_user_id:
166 reference.reference_type = ReferenceType.surfed
167 reference.to_user_id = host_request.host_user_id
168 assert from_user_id == host_request.surfer_user_id
169 else:
170 reference.reference_type = ReferenceType.hosted
171 reference.to_user_id = host_request.surfer_user_id
172 assert from_user_id == host_request.host_user_id
174 session.add(reference)
175 session.commit()
176 return reference.id, actual_host_request_id
179def create_friend_reference(session, from_user_id, to_user_id, reference_age):
180 reference = Reference(
181 time=now() - reference_age,
182 from_user_id=from_user_id,
183 to_user_id=to_user_id,
184 reference_type=ReferenceType.friend,
185 text="Test friend request",
186 rating=0.4,
187 was_appropriate=True,
188 )
189 session.add(reference)
190 session.commit()
191 return reference.id
194def test_ListPagination(db):
195 user1, token1 = generate_user()
196 user2, token2 = generate_user()
197 user3, token3 = generate_user()
198 user4, token4 = generate_user()
199 user5, token5 = generate_user()
200 user6, token6 = generate_user()
201 user7, token7 = generate_user()
202 user8, token8 = generate_user()
203 user9, token9 = generate_user()
205 with session_scope() as session:
206 # bidirectional references
207 ref2, hr2 = create_host_reference(session, user2.id, user1.id, timedelta(days=16, seconds=110), surfing=True)
208 ref2b, _ = create_host_reference(
209 session, user1.id, user2.id, timedelta(days=16, seconds=100), host_request_id=hr2
210 )
212 ref3, _ = create_host_reference(session, user3.id, user1.id, timedelta(days=16, seconds=90), surfing=False)
213 ref4, _ = create_host_reference(session, user4.id, user1.id, timedelta(days=16, seconds=80), surfing=True)
214 ref4b = create_friend_reference(session, user1.id, user4.id, timedelta(days=16, seconds=70))
216 ref5, hr5 = create_host_reference(session, user5.id, user1.id, timedelta(days=16, seconds=60), surfing=False)
217 ref5b, _ = create_host_reference(
218 session, user1.id, user5.id, timedelta(days=16, seconds=50), host_request_id=hr5
219 )
221 ref6, _ = create_host_reference(session, user6.id, user1.id, timedelta(days=16, seconds=40), surfing=True)
223 ref7 = create_friend_reference(session, user7.id, user1.id, timedelta(days=16, seconds=30))
225 ref8, _ = create_host_reference(session, user8.id, user1.id, timedelta(days=16, seconds=20), surfing=False)
226 ref9, _ = create_host_reference(session, user9.id, user1.id, timedelta(days=16, seconds=10), surfing=False)
228 # should be visible even under 2 weeks
229 ref7b = create_friend_reference(session, user1.id, user7.id, timedelta(days=9))
231 # hidden because it's less than 2 weeks
232 ref6hidden, _ = create_host_reference(session, user6.id, user1.id, timedelta(days=5), surfing=False)
234 # visible because both were written
235 ref8b, hr8 = create_host_reference(session, user8.id, user1.id, timedelta(days=3, seconds=20), surfing=False)
236 ref8c, _ = create_host_reference(
237 session, user1.id, user8.id, timedelta(days=3, seconds=10), host_request_id=hr8
238 )
240 # note that visibility tests don't really test real logic
242 # these check the right refs are in the right requests and appear in the right order (latest first)
244 with references_session(token2) as api:
245 # written by user1
246 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id, page_size=2))
247 assert [ref.reference_id for ref in res.references] == [ref8c, ref7b]
249 res = api.ListReferences(
250 references_pb2.ListReferencesReq(from_user_id=user1.id, page_token=res.next_page_token, page_size=2)
251 )
252 assert [ref.reference_id for ref in res.references] == [ref5b, ref4b]
254 res = api.ListReferences(
255 references_pb2.ListReferencesReq(from_user_id=user1.id, page_token=res.next_page_token, page_size=2)
256 )
257 assert [ref.reference_id for ref in res.references] == [ref2b]
258 assert not res.next_page_token
260 # received by user1
261 res = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id, page_size=5))
262 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8, ref7, ref6]
264 res = api.ListReferences(
265 references_pb2.ListReferencesReq(to_user_id=user1.id, page_token=res.next_page_token, page_size=5)
266 )
267 assert [ref.reference_id for ref in res.references] == [ref5, ref4, ref3, ref2]
268 assert not res.next_page_token
270 # same thing but with filters
271 res = api.ListReferences(
272 references_pb2.ListReferencesReq(
273 to_user_id=user1.id,
274 reference_type_filter=[
275 references_pb2.REFERENCE_TYPE_HOSTED,
276 references_pb2.REFERENCE_TYPE_SURFED,
277 references_pb2.REFERENCE_TYPE_FRIEND,
278 ],
279 page_size=5,
280 )
281 )
282 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8, ref7, ref6]
284 res = api.ListReferences(
285 references_pb2.ListReferencesReq(
286 to_user_id=user1.id,
287 reference_type_filter=[
288 references_pb2.REFERENCE_TYPE_HOSTED,
289 references_pb2.REFERENCE_TYPE_SURFED,
290 references_pb2.REFERENCE_TYPE_FRIEND,
291 ],
292 page_token=res.next_page_token,
293 page_size=5,
294 )
295 )
296 assert [ref.reference_id for ref in res.references] == [ref5, ref4, ref3, ref2]
297 assert not res.next_page_token
299 # received hosting references
300 res = api.ListReferences(
301 references_pb2.ListReferencesReq(
302 to_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED], page_size=3
303 )
304 )
305 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8]
307 res = api.ListReferences(
308 references_pb2.ListReferencesReq(
309 to_user_id=user1.id,
310 reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED],
311 page_token=res.next_page_token,
312 page_size=3,
313 )
314 )
315 assert [ref.reference_id for ref in res.references] == [ref5, ref3]
316 assert not res.next_page_token
318 # written friend references
319 res = api.ListReferences(
320 references_pb2.ListReferencesReq(
321 from_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_FRIEND]
322 )
323 )
324 assert [ref.reference_id for ref in res.references] == [ref7b, ref4b]
325 assert not res.next_page_token
327 # written surfing references
328 res = api.ListReferences(
329 references_pb2.ListReferencesReq(
330 from_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_SURFED]
331 )
332 )
333 assert [ref.reference_id for ref in res.references] == [ref8c, ref5b]
334 assert not res.next_page_token
336 with references_session(token7) as api:
337 # need to set at least one of from or to user
338 with pytest.raises(grpc.RpcError) as e:
339 api.ListReferences(
340 references_pb2.ListReferencesReq(reference_type_filter=[references_pb2.REFERENCE_TYPE_SURFED])
341 )
342 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
343 assert e.value.details() == errors.NEED_TO_SPECIFY_AT_LEAST_ONE_USER
345 with references_session(token5) as api:
346 # from user1 to user2
347 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id, to_user_id=user2.id))
348 assert [ref.reference_id for ref in res.references] == [ref2b]
349 assert not res.next_page_token
351 # from user5 to user1
352 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user5.id, to_user_id=user1.id))
353 assert [ref.reference_id for ref in res.references] == [ref5]
354 assert not res.next_page_token
357def test_ListReference_banned_deleted_users(db):
358 user1, token1 = generate_user()
359 user2, token2 = generate_user()
360 user3, token3 = generate_user()
362 with session_scope() as session:
363 create_friend_reference(session, user2.id, user1.id, timedelta(days=15))
364 create_friend_reference(session, user3.id, user1.id, timedelta(days=16))
365 create_friend_reference(session, user1.id, user2.id, timedelta(days=15))
366 create_friend_reference(session, user1.id, user3.id, timedelta(days=16))
368 with references_session(token1) as api:
369 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references
370 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references
371 assert len(refs_rec) == 2
372 assert len(refs_sent) == 2
374 # ban user2
375 with session_scope() as session:
376 user2 = session.execute(select(User).where(User.username == user2.username)).scalar_one()
377 user2.is_banned = True
378 session.commit()
380 # reference to and from banned user is hidden
381 with references_session(token1) as api:
382 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references
383 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references
384 assert len(refs_rec) == 1
385 assert len(refs_sent) == 1
387 # delete user3
388 with session_scope() as session:
389 user3 = session.execute(select(User).where(User.username == user3.username)).scalar_one()
390 user3.is_deleted = True
391 session.commit()
393 # doesn't change; references to and from deleted users remain
394 with references_session(token1) as api:
395 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references
396 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references
397 assert len(refs_rec) == 1
398 assert len(refs_sent) == 1
401def test_WriteFriendReference(db):
402 user1, token1 = generate_user()
403 user2, token2 = generate_user()
404 user3, token3 = generate_user()
406 with references_session(token1) as api:
407 # can write normal friend reference
408 res = api.WriteFriendReference(
409 references_pb2.WriteFriendReferenceReq(
410 to_user_id=user2.id,
411 text="A test reference",
412 was_appropriate=True,
413 rating=0.5,
414 )
415 )
416 assert res.from_user_id == user1.id
417 assert res.to_user_id == user2.id
418 assert res.reference_type == references_pb2.REFERENCE_TYPE_FRIEND
419 assert res.text == "A test reference"
420 assert now() - timedelta(hours=24) <= to_aware_datetime(res.written_time) <= now()
421 assert not res.host_request_id
423 with references_session(token3) as api:
424 # check it shows up
425 res = api.ListReferences(
426 references_pb2.ListReferencesReq(
427 from_user_id=user1.id, to_user_id=user2.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_FRIEND]
428 )
429 )
430 assert len(res.references) == 1
431 ref = res.references[0]
432 assert ref.from_user_id == user1.id
433 assert ref.to_user_id == user2.id
434 assert ref.reference_type == references_pb2.REFERENCE_TYPE_FRIEND
435 assert ref.text == "A test reference"
436 assert now() - timedelta(hours=24) <= to_aware_datetime(ref.written_time) <= now()
437 assert not ref.host_request_id
439 with references_session(token1) as api:
440 # can't write a second friend reference
441 with pytest.raises(grpc.RpcError) as e:
442 api.WriteFriendReference(
443 references_pb2.WriteFriendReferenceReq(
444 to_user_id=user2.id,
445 text="A test reference",
446 was_appropriate=True,
447 rating=0.5,
448 )
449 )
450 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
451 assert e.value.details() == errors.REFERENCE_ALREADY_GIVEN
453 with references_session(token2) as api:
454 # can't write a reference about yourself
455 with pytest.raises(grpc.RpcError) as e:
456 api.WriteFriendReference(
457 references_pb2.WriteFriendReferenceReq(
458 to_user_id=user2.id,
459 text="I'm really awesome",
460 was_appropriate=True,
461 rating=1.0,
462 )
463 )
464 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
465 assert e.value.details() == errors.CANT_REFER_SELF
468def test_WriteFriendReference_with_empty_text(db):
469 user1, token1 = generate_user()
470 user2, token2 = generate_user()
472 with references_session(token1) as api:
473 with pytest.raises(grpc.RpcError) as e:
474 api.WriteFriendReference(
475 references_pb2.WriteFriendReferenceReq(to_user_id=user2.id, text=" ", was_appropriate=True, rating=0.8)
476 )
477 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
478 assert e.value.details() == errors.REFERENCE_NO_TEXT
481def test_WriteFriendReference_with_private_text(db, push_collector):
482 user1, token1 = generate_user()
483 user2, token2 = generate_user()
485 with references_session(token1) as api:
486 with patch("couchers.email.queue_email") as mock1:
487 with mock_notification_email() as mock2:
488 api.WriteFriendReference(
489 references_pb2.WriteFriendReferenceReq(
490 to_user_id=user2.id,
491 text="They were nice!",
492 was_appropriate=True,
493 rating=0.6,
494 private_text="A bit of an odd ball, but a nice person nonetheless.",
495 )
496 )
498 # make sure an email was sent to the user receiving the ref as well as the mods
499 assert mock1.call_count == 1
500 assert mock2.call_count == 1
501 e = email_fields(mock2)
502 assert e.subject == f"[TEST] You've received a friend reference from {user1.name}!"
503 assert e.recipient == user2.email
505 push_collector.assert_user_has_single_matching(
506 user2.id,
507 title=f"You've received a friend reference from {user1.name}!",
508 body="They were nice!",
509 )
512def test_host_request_states_references(db):
513 user1, token1 = generate_user()
514 user2, token2 = generate_user()
516 with session_scope() as session:
517 # can't write ref
518 hr1 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.pending)
519 # can write ref
520 hr2 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.accepted)
521 # can't write ref
522 hr3 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.rejected)
523 # can write ref
524 hr4 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.confirmed)
525 # can't write ref
526 hr5 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.cancelled)
528 with references_session(token1) as api:
529 # pending
530 api.WriteHostRequestReference(
531 references_pb2.WriteHostRequestReferenceReq(
532 host_request_id=hr2,
533 text="Should work!",
534 was_appropriate=True,
535 rating=0.9,
536 )
537 )
539 # accepted
540 api.WriteHostRequestReference(
541 references_pb2.WriteHostRequestReferenceReq(
542 host_request_id=hr4,
543 text="Should work!",
544 was_appropriate=True,
545 rating=0.9,
546 )
547 )
549 # rejected
550 with pytest.raises(grpc.RpcError) as e:
551 api.WriteHostRequestReference(
552 references_pb2.WriteHostRequestReferenceReq(
553 host_request_id=hr1,
554 text="Shouldn't work...",
555 was_appropriate=True,
556 rating=0.9,
557 )
558 )
559 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
560 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST
562 # confirmed
563 with pytest.raises(grpc.RpcError) as e:
564 api.WriteHostRequestReference(
565 references_pb2.WriteHostRequestReferenceReq(
566 host_request_id=hr3,
567 text="Shouldn't work...",
568 was_appropriate=True,
569 rating=0.9,
570 )
571 )
572 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
573 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST
575 # cancelled
576 with pytest.raises(grpc.RpcError) as e:
577 api.WriteHostRequestReference(
578 references_pb2.WriteHostRequestReferenceReq(
579 host_request_id=hr5,
580 text="Shouldn't work...",
581 was_appropriate=True,
582 rating=0.9,
583 )
584 )
585 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
586 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST
589def test_WriteHostRequestReference(db):
590 user1, token1 = generate_user()
591 user2, token2 = generate_user()
592 user3, token3 = generate_user()
593 user4, token4 = generate_user()
595 with session_scope() as session:
596 # too old
597 hr1 = create_host_request(session, user3.id, user1.id, timedelta(days=20))
598 # valid host req, surfer said we didn't show up but we can still write a req
599 hr2 = create_host_request(session, user3.id, user1.id, timedelta(days=10), surfer_reason_didnt_meetup="No show")
600 # valid surfing req
601 hr3 = create_host_request(session, user1.id, user3.id, timedelta(days=7))
602 # not yet complete
603 hr4 = create_host_request(session, user2.id, user1.id, timedelta(days=1), status=HostRequestStatus.pending)
604 # we indicated we didn't meet
605 hr5 = create_host_request(session, user4.id, user1.id, timedelta(days=7), host_reason_didnt_meetup="")
606 # we will indicate we didn't meet
607 hr6 = create_host_request(session, user4.id, user1.id, timedelta(days=8))
609 with references_session(token3) as api:
610 # can write for this one
611 api.WriteHostRequestReference(
612 references_pb2.WriteHostRequestReferenceReq(
613 host_request_id=hr3,
614 text="Should work!",
615 was_appropriate=True,
616 rating=0.9,
617 )
618 )
620 with references_session(token1) as api:
621 # can't write reference for a HR that's not yet finished
622 with pytest.raises(grpc.RpcError) as e:
623 api.WriteHostRequestReference(
624 references_pb2.WriteHostRequestReferenceReq(
625 host_request_id=hr4,
626 text="Shouldn't work...",
627 was_appropriate=True,
628 rating=0.9,
629 )
630 )
631 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
632 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST
634 # can't write reference that's more than 2 weeks old
635 with pytest.raises(grpc.RpcError) as e:
636 api.WriteHostRequestReference(
637 references_pb2.WriteHostRequestReferenceReq(
638 host_request_id=hr1,
639 text="Shouldn't work...",
640 was_appropriate=True,
641 rating=0.9,
642 )
643 )
644 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
645 assert e.value.details() == errors.CANT_WRITE_REFERENCE_FOR_REQUEST
647 # can write for this one
648 api.WriteHostRequestReference(
649 references_pb2.WriteHostRequestReferenceReq(
650 host_request_id=hr2,
651 text="Should work!",
652 was_appropriate=True,
653 rating=0.9,
654 )
655 )
657 # but can't write a second one for the same one
658 with pytest.raises(grpc.RpcError) as e:
659 api.WriteHostRequestReference(
660 references_pb2.WriteHostRequestReferenceReq(
661 host_request_id=hr2,
662 text="Shouldn't work...",
663 was_appropriate=True,
664 rating=0.9,
665 )
666 )
667 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
668 assert e.value.details() == errors.REFERENCE_ALREADY_GIVEN
670 # can write for this one too
671 api.WriteHostRequestReference(
672 references_pb2.WriteHostRequestReferenceReq(
673 host_request_id=hr3,
674 text="Should work!",
675 was_appropriate=True,
676 rating=0.9,
677 )
678 )
680 # can't write reference for a HR that we indicated we didn't show up
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() == errors.CANT_WRITE_REFERENCE_INDICATED_DIDNT_MEETUP
693 # can't write reference for a HR that we indicate we didn't show up for
694 api.HostRequestIndicateDidntMeetup(
695 references_pb2.HostRequestIndicateDidntMeetupReq(
696 host_request_id=hr6,
697 reason_didnt_meetup="No clue?",
698 )
699 )
701 with pytest.raises(grpc.RpcError) as e:
702 api.WriteHostRequestReference(
703 references_pb2.WriteHostRequestReferenceReq(
704 host_request_id=hr6,
705 text="Shouldn't work...",
706 was_appropriate=True,
707 rating=0.9,
708 )
709 )
710 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
711 assert e.value.details() == errors.CANT_WRITE_REFERENCE_INDICATED_DIDNT_MEETUP
713 with references_session(token4) as api:
714 # they can still write one
715 api.WriteHostRequestReference(
716 references_pb2.WriteHostRequestReferenceReq(
717 host_request_id=hr6,
718 text="Should work!",
719 was_appropriate=True,
720 rating=0.9,
721 )
722 )
725def test_WriteHostRequestReference_private_text(db, push_collector):
726 user1, token1 = generate_user()
727 user2, token2 = generate_user()
729 with session_scope() as session:
730 hr = create_host_request(session, user1.id, user2.id, timedelta(days=10))
732 with references_session(token1) as api:
733 with patch("couchers.email.queue_email") as mock1:
734 with mock_notification_email() as mock2:
735 api.WriteHostRequestReference(
736 references_pb2.WriteHostRequestReferenceReq(
737 host_request_id=hr,
738 text="Should work!",
739 was_appropriate=True,
740 rating=0.9,
741 private_text="Something",
742 )
743 )
745 # make sure an email was sent to the user receiving the ref as well as the mods
746 assert mock1.call_count == 1
747 assert mock2.call_count == 1
749 e = email_fields(mock2)
750 assert e.subject == f"[TEST] You've received a reference from {user1.name}!"
751 assert e.recipient == user2.email
753 push_collector.assert_user_has_single_matching(
754 user2.id,
755 title=f"You've received a reference from {user1.name}!",
756 body="Please go and write a reference for them too. It's a nice gesture and helps us build a community together!",
757 )
760def test_AvailableWriteReferences_and_ListPendingReferencesToWrite(db):
761 user1, token1 = generate_user()
762 user2, token2 = generate_user()
763 user3, token3 = generate_user()
764 user4, token4 = generate_user()
765 user5, token5 = generate_user(delete_user=True)
766 user6, token6 = generate_user()
767 user7, token7 = generate_user()
768 user8, token8 = generate_user()
769 user9, token9 = generate_user()
770 user10, token10 = generate_user()
771 user11, token11 = generate_user()
772 make_user_block(user1, user6)
773 make_user_block(user7, user1)
775 with session_scope() as session:
776 # too old
777 hr1 = create_host_request(session, user3.id, user1.id, timedelta(days=20))
779 # already wrote friend ref to user3
780 create_friend_reference(session, user1.id, user3.id, timedelta(days=15, seconds=70))
782 # already given
783 _, hr2 = create_host_reference(session, user2.id, user1.id, timedelta(days=10, seconds=110), surfing=True)
784 create_host_reference(session, user1.id, user2.id, timedelta(days=10, seconds=100), host_request_id=hr2)
786 # valid hosted
787 hr3 = create_host_request(session, user3.id, user1.id, timedelta(days=8))
789 # valid surfed
790 hr4 = create_host_request(session, user1.id, user4.id, timedelta(days=5))
792 # not yet complete
793 hr5 = create_host_request(session, user2.id, user1.id, timedelta(days=2), status=HostRequestStatus.pending)
795 # already wrote friend ref to user2
796 create_friend_reference(session, user1.id, user2.id, timedelta(days=1))
798 # user5 deleted, reference won't show up as pending
799 create_host_request(session, user1.id, user5.id, timedelta(days=5))
801 # user6 blocked, reference won't show up as pending
802 create_host_request(session, user1.id, user6.id, timedelta(days=5))
804 # user7 blocking, reference won't show up as pending
805 create_host_request(session, user1.id, user7.id, timedelta(days=5))
807 # hosted but we indicated we didn't meet up, no reason; should not show up
808 create_host_request(session, user8.id, user1.id, timedelta(days=11), host_reason_didnt_meetup="")
810 # surfed but we indicated we didn't meet up, has reason; should not show up
811 create_host_request(
812 session, user1.id, user9.id, timedelta(days=10), surfer_reason_didnt_meetup="They never showed up!"
813 )
815 # surfed but they indicated we didn't meet up, no reason; should show up
816 hr6 = create_host_request(session, user1.id, user10.id, timedelta(days=4), host_reason_didnt_meetup="")
818 # hosted but they indicated we didn't meet up, has reason; should show up
819 hr7 = create_host_request(
820 session, user11.id, user1.id, timedelta(days=3), surfer_reason_didnt_meetup="They never showed up!!"
821 )
823 with references_session(token1) as api:
824 # can't write reference for invisible user
825 with pytest.raises(grpc.RpcError) as e:
826 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user5.id))
827 assert e.value.code() == grpc.StatusCode.NOT_FOUND
828 assert e.value.details() == errors.USER_NOT_FOUND
830 # can't write reference for blocking user
831 with pytest.raises(grpc.RpcError) as e:
832 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user7.id))
833 assert e.value.code() == grpc.StatusCode.NOT_FOUND
834 assert e.value.details() == errors.USER_NOT_FOUND
836 # can't write reference for blocked user
837 with pytest.raises(grpc.RpcError) as e:
838 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user6.id))
839 assert e.value.code() == grpc.StatusCode.NOT_FOUND
840 assert e.value.details() == errors.USER_NOT_FOUND
842 # can't write anything to myself
843 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
844 assert not res.can_write_friend_reference
845 assert len(res.available_write_references) == 0
847 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
848 # can't write friend ref to user2
849 assert not res.can_write_friend_reference
850 # none we can write for user2
851 assert len(res.available_write_references) == 0
853 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user3.id))
854 # can't write friend ref to user3
855 assert not res.can_write_friend_reference
856 # can write one reference because we hosted user3
857 assert len(res.available_write_references) == 1
858 w = res.available_write_references[0]
859 assert w.host_request_id == hr3
860 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
861 assert now() + timedelta(days=6) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=7)
863 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user4.id))
864 # can write friend ref to user4
865 assert res.can_write_friend_reference
866 # can write one reference because we surfed with user4
867 assert len(res.available_write_references) == 1
868 w = res.available_write_references[0]
869 assert w.host_request_id == hr4
870 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
871 assert now() + timedelta(days=9) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=10)
873 # can't write a req if we indicated we didn't meet up
874 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user8.id))
875 assert len(res.available_write_references) == 0
876 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user9.id))
877 assert len(res.available_write_references) == 0
879 # can still write ref if the other person indicated we didn't meet up
880 # surfed with them
881 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user10.id))
882 assert len(res.available_write_references) == 1
883 w = res.available_write_references[0]
884 assert w.host_request_id == hr6
885 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
886 assert now() + timedelta(days=10) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=11)
887 # hosted them
888 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user11.id))
889 assert len(res.available_write_references) == 1
890 w = res.available_write_references[0]
891 assert w.host_request_id == hr7
892 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
893 assert now() + timedelta(days=11) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=12)
895 # finally check the general list
896 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
897 assert len(res.pending_references) == 4
898 w = res.pending_references[0]
899 assert w.host_request_id == hr3
900 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
901 assert now() + timedelta(days=6) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=7)
902 w = res.pending_references[1]
903 assert w.host_request_id == hr4
904 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
905 assert now() + timedelta(days=9) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=10)
906 w = res.pending_references[2]
907 assert w.host_request_id == hr6
908 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
909 assert now() + timedelta(days=10) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=11)
910 w = res.pending_references[3]
911 assert w.host_request_id == hr7
912 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
913 assert now() + timedelta(days=11) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=12)
916@pytest.mark.parametrize("hs", ["host", "surfer"])
917def test_regression_disappearing_refs(db, hs):
918 """
919 Roughly the reproduction steps are:
920 * Send a host request, then have both host and surfer accept
921 * Wait for it to elapse (or hack it with SQL like what you told me to do)
922 * On the surfer account, leave a reference
923 * Then on the host account, the option to leave a reference is then not available
924 """
925 user1, token1 = generate_user()
926 user2, token2 = generate_user()
927 req_start = (today() + timedelta(days=2)).isoformat()
928 req_end = (today() + timedelta(days=3)).isoformat()
929 with requests_session(token1) as api:
930 res = api.CreateHostRequest(
931 requests_pb2.CreateHostRequestReq(
932 host_user_id=user2.id, from_date=req_start, to_date=req_end, text="Test request"
933 )
934 )
935 host_request_id = res.host_request_id
936 assert (
937 api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True))
938 .host_requests[0]
939 .latest_message.text.text
940 == "Test request"
941 )
943 with requests_session(token2) as api:
944 api.RespondHostRequest(
945 requests_pb2.RespondHostRequestReq(
946 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
947 )
948 )
950 with requests_session(token1) as api:
951 api.RespondHostRequest(
952 requests_pb2.RespondHostRequestReq(
953 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
954 )
955 )
957 with references_session(token1) as api:
958 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
959 assert len(res.pending_references) == 0
960 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
961 assert len(res.available_write_references) == 0
963 with references_session(token2) as api:
964 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
965 assert len(res.pending_references) == 0
966 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
967 assert len(res.available_write_references) == 0
969 # hack the time backwards
970 hack_req_start = today() - timedelta(days=10) + timedelta(days=2)
971 hack_req_end = today() - timedelta(days=10) + timedelta(days=3)
972 with session_scope() as session:
973 host_request = session.execute(select(HostRequest)).scalar_one()
974 assert host_request.conversation_id == host_request_id
975 host_request.from_date = hack_req_start
976 host_request.to_date = hack_req_end
978 with references_session(token1) as api:
979 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
980 assert len(res.pending_references) == 1
981 assert res.pending_references[0].host_request_id == host_request_id
982 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
984 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
985 assert len(res.available_write_references) == 1
986 assert res.available_write_references[0].host_request_id == host_request_id
987 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
989 with references_session(token2) as api:
990 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
991 assert len(res.pending_references) == 1
992 assert res.pending_references[0].host_request_id == host_request_id
993 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED
995 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
996 assert len(res.available_write_references) == 1
997 assert res.available_write_references[0].host_request_id == host_request_id
998 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1000 if hs == "host":
1001 with references_session(token2) as api:
1002 api.WriteHostRequestReference(
1003 references_pb2.WriteHostRequestReferenceReq(
1004 host_request_id=host_request_id,
1005 text="Good stuff",
1006 was_appropriate=True,
1007 rating=0.86,
1008 )
1009 )
1011 with references_session(token2) as api:
1012 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1013 assert len(res.pending_references) == 0
1015 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
1016 assert len(res.available_write_references) == 0
1018 with references_session(token1) as api:
1019 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1020 assert len(res.pending_references) == 1
1021 assert res.pending_references[0].host_request_id == host_request_id
1022 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
1024 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
1025 assert len(res.available_write_references) == 1
1026 assert res.available_write_references[0].host_request_id == host_request_id
1027 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
1028 else:
1029 with references_session(token1) as api:
1030 api.WriteHostRequestReference(
1031 references_pb2.WriteHostRequestReferenceReq(
1032 host_request_id=host_request_id,
1033 text="Good stuff",
1034 was_appropriate=True,
1035 rating=0.86,
1036 )
1037 )
1039 with references_session(token1) as api:
1040 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1041 assert len(res.pending_references) == 0
1043 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
1044 assert len(res.available_write_references) == 0
1046 with references_session(token2) as api:
1047 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1048 assert len(res.pending_references) == 1
1049 assert res.pending_references[0].host_request_id == host_request_id
1050 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1052 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
1053 assert len(res.available_write_references) == 1
1054 assert res.available_write_references[0].host_request_id == host_request_id
1055 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED