Coverage for app/backend/src/tests/test_references.py: 100%
649 statements
« prev ^ index » next coverage.py v7.14.2, created at 2026-06-21 09:29 +0000
« prev ^ index » next coverage.py v7.14.2, created at 2026-06-21 09:29 +0000
1from datetime import date, datetime, timedelta
3import grpc
4import pytest
5from google.protobuf import empty_pb2
6from sqlalchemy import func, select, update
7from sqlalchemy.orm import Session
9from couchers.db import session_scope
10from couchers.materialized_views import refresh_materialized_views_rapid
11from couchers.models import (
12 Conversation,
13 FriendRelationship,
14 FriendStatus,
15 HostRequest,
16 HostRequestStatus,
17 Message,
18 MessageType,
19 ModerationObjectType,
20 ModerationState,
21 ModerationVisibility,
22 Reference,
23 ReferenceType,
24 User,
25)
26from couchers.moderation.utils import create_moderation
27from couchers.proto import conversations_pb2, moderation_pb2, references_pb2, requests_pb2
28from couchers.utils import create_coordinate, now, to_aware_datetime, today
29from tests.fixtures.db import generate_user, make_friends, make_user_block
30from tests.fixtures.misc import EmailCollector, PushCollector
31from tests.fixtures.sessions import account_session, real_moderation_session, references_session, requests_session
32from tests.test_requests import valid_request_text
35@pytest.fixture(autouse=True)
36def _(testconfig):
37 pass
40def create_host_request(
41 session: Session,
42 surfer_user_id: int,
43 host_user_id: int,
44 host_request_age: timedelta = timedelta(days=15),
45 status: HostRequestStatus = HostRequestStatus.confirmed,
46 host_reason_didnt_meetup: str | None = None,
47 surfer_reason_didnt_meetup: str | None = None,
48) -> int:
49 """
50 Create a host request that's `host_request_age` old
51 """
52 from_date = today() - host_request_age - timedelta(days=2)
53 to_date = today() - host_request_age
54 fake_created = now() - host_request_age - timedelta(days=3)
55 conversation = Conversation()
56 session.add(conversation)
57 session.flush()
59 msg1 = Message(
60 conversation_id=conversation.id,
61 author_id=surfer_user_id,
62 message_type=MessageType.chat_created,
63 )
64 msg1.time = fake_created + timedelta(seconds=1)
65 session.add(msg1)
67 msg2 = Message(
68 conversation_id=conversation.id,
69 author_id=surfer_user_id,
70 text="Hi, I'm requesting to be hosted.",
71 message_type=MessageType.text,
72 )
73 msg2.time = fake_created + timedelta(seconds=2)
74 session.add(msg2)
75 session.flush()
77 moderation_state = create_moderation(
78 session,
79 ModerationObjectType.host_request,
80 conversation.id,
81 surfer_user_id,
82 )
84 host_request = HostRequest(
85 conversation_id=conversation.id,
86 initiator_user_id=surfer_user_id,
87 recipient_user_id=host_user_id,
88 from_date=from_date,
89 to_date=to_date,
90 status=status,
91 initiator_last_seen_message_id=msg2.id,
92 recipient_reason_didnt_meetup=host_reason_didnt_meetup,
93 initiator_reason_didnt_meetup=surfer_reason_didnt_meetup,
94 hosting_city="Test City",
95 hosting_location=create_coordinate(0, 0),
96 hosting_radius=10,
97 moderation_state_id=moderation_state.id,
98 )
99 session.add(host_request)
100 session.commit()
101 return host_request.conversation_id
104def create_host_request_by_date(
105 session: Session,
106 surfer_user_id: int,
107 host_user_id: int,
108 from_date: date,
109 to_date: date,
110 status: HostRequestStatus,
111 host_sent_request_reminders: int,
112 last_sent_request_reminder_time: datetime,
113) -> int:
114 conversation = Conversation()
115 session.add(conversation)
116 session.flush()
118 msg1 = Message(
119 conversation_id=conversation.id,
120 author_id=surfer_user_id,
121 message_type=MessageType.chat_created,
122 )
123 msg1.time = from_date + timedelta(seconds=1) # type: ignore[assignment]
124 session.add(msg1)
126 # Unused for now, but every host request must have a message.
127 msg2 = Message(
128 conversation_id=conversation.id,
129 author_id=surfer_user_id,
130 text="Hi, I'm requesting to be hosted.",
131 message_type=MessageType.text,
132 )
133 msg2.time = from_date + timedelta(seconds=2) # type: ignore[assignment]
134 session.add(msg2)
135 session.flush()
137 moderation_state = create_moderation(
138 session,
139 ModerationObjectType.host_request,
140 conversation.id,
141 surfer_user_id,
142 )
144 host_request = HostRequest(
145 conversation_id=conversation.id,
146 initiator_user_id=surfer_user_id,
147 recipient_user_id=host_user_id,
148 from_date=from_date,
149 to_date=to_date,
150 status=status,
151 hosting_city="Test City",
152 hosting_location=create_coordinate(0, 0),
153 hosting_radius=10,
154 moderation_state_id=moderation_state.id,
155 )
156 host_request.recipient_sent_request_reminders = host_sent_request_reminders
157 host_request.last_sent_request_reminder_time = last_sent_request_reminder_time
159 session.add(host_request)
160 session.commit()
161 return host_request.conversation_id
164def create_host_reference(
165 session: Session,
166 from_user_id: int,
167 to_user_id: int,
168 reference_age: timedelta,
169 *,
170 surfing: bool = True,
171 host_request_id: int | None = None,
172) -> tuple[int, int]:
173 if host_request_id:
174 actual_host_request_id = host_request_id
175 else:
176 if surfing:
177 actual_host_request_id = host_request_id or create_host_request(
178 session, from_user_id, to_user_id, reference_age + timedelta(days=1)
179 )
180 else:
181 actual_host_request_id = host_request_id or create_host_request(
182 session, to_user_id, from_user_id, reference_age + timedelta(days=1)
183 )
185 host_request = session.execute(
186 select(HostRequest).where(HostRequest.conversation_id == actual_host_request_id)
187 ).scalar_one()
189 if host_request.initiator_user_id == from_user_id:
190 reference_type = ReferenceType.surfed
191 to_user_id = host_request.recipient_user_id
192 assert from_user_id == host_request.initiator_user_id
193 else:
194 reference_type = ReferenceType.hosted
195 to_user_id = host_request.initiator_user_id
196 assert from_user_id == host_request.recipient_user_id
198 moderation_state = ModerationState(
199 object_type=ModerationObjectType.reference,
200 object_id=0, # placeholder, set after Reference flush
201 visibility=ModerationVisibility.visible,
202 )
203 session.add(moderation_state)
204 session.flush()
206 reference = Reference(
207 from_user_id=from_user_id,
208 to_user_id=to_user_id,
209 host_request_id=host_request.conversation_id,
210 text="Dummy reference",
211 rating=0.5,
212 was_appropriate=True,
213 reference_type=reference_type,
214 moderation_state_id=moderation_state.id,
215 )
216 reference.time = now() - reference_age
218 session.add(reference)
219 session.flush()
220 moderation_state.object_id = reference.id
221 session.commit()
222 return reference.id, actual_host_request_id
225def create_friend_reference(session: Session, from_user_id: int, to_user_id: int, reference_age: timedelta) -> int:
226 moderation_state = ModerationState(
227 object_type=ModerationObjectType.reference,
228 object_id=0, # placeholder, set after Reference flush
229 visibility=ModerationVisibility.visible,
230 )
231 session.add(moderation_state)
232 session.flush()
234 reference = Reference(
235 from_user_id=from_user_id,
236 to_user_id=to_user_id,
237 reference_type=ReferenceType.friend,
238 text="Test friend request",
239 rating=0.4,
240 was_appropriate=True,
241 moderation_state_id=moderation_state.id,
242 )
243 reference.time = now() - reference_age
244 session.add(reference)
245 session.flush()
246 moderation_state.object_id = reference.id
247 session.commit()
248 return reference.id
251def test_ListPagination(db):
252 user1, token1 = generate_user()
253 user2, token2 = generate_user()
254 user3, token3 = generate_user()
255 user4, token4 = generate_user()
256 user5, token5 = generate_user()
257 user6, token6 = generate_user()
258 user7, token7 = generate_user()
259 user8, token8 = generate_user()
260 user9, token9 = generate_user()
262 with session_scope() as session:
263 # bidirectional references
264 ref2, hr2 = create_host_reference(session, user2.id, user1.id, timedelta(days=16, seconds=110), surfing=True)
265 ref2b, _ = create_host_reference(
266 session, user1.id, user2.id, timedelta(days=16, seconds=100), host_request_id=hr2
267 )
269 ref3, _ = create_host_reference(session, user3.id, user1.id, timedelta(days=16, seconds=90), surfing=False)
270 ref4, _ = create_host_reference(session, user4.id, user1.id, timedelta(days=16, seconds=80), surfing=True)
271 ref4b = create_friend_reference(session, user1.id, user4.id, timedelta(days=16, seconds=70))
273 ref5, hr5 = create_host_reference(session, user5.id, user1.id, timedelta(days=16, seconds=60), surfing=False)
274 ref5b, _ = create_host_reference(
275 session, user1.id, user5.id, timedelta(days=16, seconds=50), host_request_id=hr5
276 )
278 ref6, _ = create_host_reference(session, user6.id, user1.id, timedelta(days=16, seconds=40), surfing=True)
280 ref7 = create_friend_reference(session, user7.id, user1.id, timedelta(days=16, seconds=30))
282 ref8, _ = create_host_reference(session, user8.id, user1.id, timedelta(days=16, seconds=20), surfing=False)
283 ref9, _ = create_host_reference(session, user9.id, user1.id, timedelta(days=16, seconds=10), surfing=False)
285 # should be visible even under 2 weeks
286 ref7b = create_friend_reference(session, user1.id, user7.id, timedelta(days=9))
288 # hidden because it's less than 2 weeks
289 ref6hidden, _ = create_host_reference(session, user6.id, user1.id, timedelta(days=5), surfing=False)
291 # visible because both were written
292 ref8b, hr8 = create_host_reference(session, user8.id, user1.id, timedelta(days=3, seconds=20), surfing=False)
293 ref8c, _ = create_host_reference(
294 session, user1.id, user8.id, timedelta(days=3, seconds=10), host_request_id=hr8
295 )
297 # note that visibility tests don't really test real logic
299 # these check the right refs are in the right requests and appear in the right order (latest first)
301 with references_session(token2) as api:
302 # written by user1
303 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id, page_size=2))
304 assert [ref.reference_id for ref in res.references] == [ref8c, ref7b]
306 res = api.ListReferences(
307 references_pb2.ListReferencesReq(from_user_id=user1.id, page_token=res.next_page_token, page_size=2)
308 )
309 assert [ref.reference_id for ref in res.references] == [ref5b, ref4b]
311 res = api.ListReferences(
312 references_pb2.ListReferencesReq(from_user_id=user1.id, page_token=res.next_page_token, page_size=2)
313 )
314 assert [ref.reference_id for ref in res.references] == [ref2b]
315 assert not res.next_page_token
317 # received by user1
318 res = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id, page_size=5))
319 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8, ref7, ref6]
321 res = api.ListReferences(
322 references_pb2.ListReferencesReq(to_user_id=user1.id, page_token=res.next_page_token, page_size=5)
323 )
324 assert [ref.reference_id for ref in res.references] == [ref5, ref4, ref3, ref2]
325 assert not res.next_page_token
327 # same thing but with filters
328 res = api.ListReferences(
329 references_pb2.ListReferencesReq(
330 to_user_id=user1.id,
331 reference_type_filter=[
332 references_pb2.REFERENCE_TYPE_HOSTED,
333 references_pb2.REFERENCE_TYPE_SURFED,
334 references_pb2.REFERENCE_TYPE_FRIEND,
335 ],
336 page_size=5,
337 )
338 )
339 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8, ref7, ref6]
341 res = api.ListReferences(
342 references_pb2.ListReferencesReq(
343 to_user_id=user1.id,
344 reference_type_filter=[
345 references_pb2.REFERENCE_TYPE_HOSTED,
346 references_pb2.REFERENCE_TYPE_SURFED,
347 references_pb2.REFERENCE_TYPE_FRIEND,
348 ],
349 page_token=res.next_page_token,
350 page_size=5,
351 )
352 )
353 assert [ref.reference_id for ref in res.references] == [ref5, ref4, ref3, ref2]
354 assert not res.next_page_token
356 # received hosting references
357 res = api.ListReferences(
358 references_pb2.ListReferencesReq(
359 to_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED], page_size=3
360 )
361 )
362 assert [ref.reference_id for ref in res.references] == [ref8b, ref9, ref8]
364 res = api.ListReferences(
365 references_pb2.ListReferencesReq(
366 to_user_id=user1.id,
367 reference_type_filter=[references_pb2.REFERENCE_TYPE_HOSTED],
368 page_token=res.next_page_token,
369 page_size=3,
370 )
371 )
372 assert [ref.reference_id for ref in res.references] == [ref5, ref3]
373 assert not res.next_page_token
375 # written friend references
376 res = api.ListReferences(
377 references_pb2.ListReferencesReq(
378 from_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_FRIEND]
379 )
380 )
381 assert [ref.reference_id for ref in res.references] == [ref7b, ref4b]
382 assert not res.next_page_token
384 # written surfing references
385 res = api.ListReferences(
386 references_pb2.ListReferencesReq(
387 from_user_id=user1.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_SURFED]
388 )
389 )
390 assert [ref.reference_id for ref in res.references] == [ref8c, ref5b]
391 assert not res.next_page_token
393 with references_session(token7) as api:
394 # need to set at least one of from or to user
395 with pytest.raises(grpc.RpcError) as e:
396 api.ListReferences(
397 references_pb2.ListReferencesReq(reference_type_filter=[references_pb2.REFERENCE_TYPE_SURFED])
398 )
399 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
400 assert e.value.details() == "You need to specify at least one user."
402 with references_session(token5) as api:
403 # from user1 to user2
404 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id, to_user_id=user2.id))
405 assert [ref.reference_id for ref in res.references] == [ref2b]
406 assert not res.next_page_token
408 # from user5 to user1
409 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user5.id, to_user_id=user1.id))
410 assert [ref.reference_id for ref in res.references] == [ref5]
411 assert not res.next_page_token
414def test_ListReference_banned_deleted_users(db):
415 user1, token1 = generate_user()
416 user2, token2 = generate_user()
417 user3, token3 = generate_user()
419 with session_scope() as session:
420 create_friend_reference(session, user2.id, user1.id, timedelta(days=15))
421 create_friend_reference(session, user3.id, user1.id, timedelta(days=16))
422 create_friend_reference(session, user1.id, user2.id, timedelta(days=15))
423 create_friend_reference(session, user1.id, user3.id, timedelta(days=16))
425 with references_session(token1) as api:
426 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references
427 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references
428 assert len(refs_rec) == 2
429 assert len(refs_sent) == 2
431 # ban user2
432 with session_scope() as session:
433 session.execute(update(User).where(User.username == user2.username).values(banned_at=func.now()))
435 # reference to and from banned user is hidden
436 with references_session(token1) as api:
437 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references
438 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references
439 assert len(refs_rec) == 1
440 assert len(refs_sent) == 1
442 # delete user3
443 with session_scope() as session:
444 session.execute(update(User).where(User.username == user3.username).values(deleted_at=func.now()))
446 # doesn't change; references to and from deleted users remain
447 with references_session(token1) as api:
448 refs_rec = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user1.id)).references
449 refs_sent = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references
450 assert len(refs_rec) == 1
451 assert len(refs_sent) == 1
454def test_WriteFriendReference(db, moderator):
455 user1, token1 = generate_user()
456 user2, token2 = generate_user()
457 user3, token3 = generate_user()
459 # Make user1 and user2 friends
460 make_friends(user1, user2)
462 with references_session(token1) as api:
463 # can write normal friend reference
464 res = api.WriteFriendReference(
465 references_pb2.WriteFriendReferenceReq(
466 to_user_id=user2.id,
467 text="A test reference",
468 was_appropriate=True,
469 rating=0.5,
470 )
471 )
472 assert res.from_user_id == user1.id
473 assert res.to_user_id == user2.id
474 assert res.reference_type == references_pb2.REFERENCE_TYPE_FRIEND
475 assert res.text == "A test reference"
476 assert now() - timedelta(hours=24) <= to_aware_datetime(res.written_time) <= now()
477 assert not res.host_request_id
479 moderator.approve_reference(res.reference_id)
481 with references_session(token3) as api:
482 # check it shows up
483 res = api.ListReferences(
484 references_pb2.ListReferencesReq(
485 from_user_id=user1.id, to_user_id=user2.id, reference_type_filter=[references_pb2.REFERENCE_TYPE_FRIEND]
486 )
487 )
488 assert len(res.references) == 1
489 ref = res.references[0]
490 assert ref.from_user_id == user1.id
491 assert ref.to_user_id == user2.id
492 assert ref.reference_type == references_pb2.REFERENCE_TYPE_FRIEND
493 assert ref.text == "A test reference"
494 assert now() - timedelta(hours=24) <= to_aware_datetime(ref.written_time) <= now()
495 assert not ref.host_request_id
497 with references_session(token1) as api:
498 # can't write a second friend reference
499 with pytest.raises(grpc.RpcError) as e:
500 api.WriteFriendReference(
501 references_pb2.WriteFriendReferenceReq(
502 to_user_id=user2.id,
503 text="A test reference",
504 was_appropriate=True,
505 rating=0.5,
506 )
507 )
508 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
509 assert e.value.details() == "Reference already given."
511 with references_session(token2) as api:
512 # can't write a reference about yourself
513 with pytest.raises(grpc.RpcError) as e:
514 api.WriteFriendReference(
515 references_pb2.WriteFriendReferenceReq(
516 to_user_id=user2.id,
517 text="I'm really awesome",
518 was_appropriate=True,
519 rating=1.0,
520 )
521 )
522 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
523 assert e.value.details() == "You can't refer yourself."
526def test_WriteFriendReference_with_empty_text(db):
527 user1, token1 = generate_user()
528 user2, token2 = generate_user()
530 with references_session(token1) as api:
531 with pytest.raises(grpc.RpcError) as e:
532 api.WriteFriendReference(
533 references_pb2.WriteFriendReferenceReq(to_user_id=user2.id, text=" ", was_appropriate=True, rating=0.8)
534 )
535 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
536 assert e.value.details() == "The text of a reference must not be empty"
539def test_WriteFriendReference_with_private_text(
540 db, email_collector: EmailCollector, push_collector: PushCollector, moderator
541):
542 user1, token1 = generate_user()
543 user2, token2 = generate_user()
545 # Make users friends
546 make_friends(user1, user2)
548 with references_session(token1) as api:
549 ref = api.WriteFriendReference(
550 references_pb2.WriteFriendReferenceReq(
551 to_user_id=user2.id,
552 text="They were nice!",
553 was_appropriate=True,
554 rating=0.6,
555 private_text="A bit of an odd ball, but a nice person nonetheless.",
556 )
557 )
558 # Approve before patches/jobs exit so the pending notification is delivered while mocked.
559 moderator.approve_reference(ref.reference_id)
561 # make sure an email was sent to the user receiving the ref as well as the mods
562 email_collector.pop_for_reports(last=True)
563 email = email_collector.pop_for_recipient(user2.email, last=True)
564 assert email.subject == f"[TEST] You've received a friend reference from {user1.name}!"
565 assert email.recipient == user2.email
567 push = push_collector.pop_for_user(user2.id, last=True)
568 assert push.content.title == f"New friend reference from {user1.name}"
569 assert push.content.body == "They were nice!"
572def test_WriteFriendReference_requires_friendship(db):
573 """Test that users must be friends to write friend references"""
574 user1, token1 = generate_user()
575 user2, token2 = generate_user()
577 # Try to write friend reference without being friends
578 with references_session(token1) as api:
579 with pytest.raises(grpc.RpcError) as e:
580 api.WriteFriendReference(
581 references_pb2.WriteFriendReferenceReq(
582 to_user_id=user2.id,
583 text="A test reference",
584 was_appropriate=True,
585 rating=0.5,
586 )
587 )
588 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
589 assert e.value.details() == "You can only write friend references for confirmed friends."
591 # Now make them friends
592 make_friends(user1, user2)
594 # Should now be able to write a reference
595 with references_session(token1) as api:
596 res = api.WriteFriendReference(
597 references_pb2.WriteFriendReferenceReq(
598 to_user_id=user2.id,
599 text="A test reference",
600 was_appropriate=True,
601 rating=0.5,
602 )
603 )
604 assert res.from_user_id == user1.id
605 assert res.to_user_id == user2.id
607 # Test the unfriending scenario: delete the friendship
608 with session_scope() as session:
609 # Change the friendship status to cancelled (simulating unfriending)
610 session.execute(
611 update(FriendRelationship)
612 .where(FriendRelationship.from_user_id == user1.id, FriendRelationship.to_user_id == user2.id)
613 .values(status=FriendStatus.cancelled)
614 )
616 # Try to write another friend reference after unfriending
617 # (Note: This assumes user1 didn't already write a reference, or we test with user2 writing to user1)
618 with references_session(token2) as api:
619 with pytest.raises(grpc.RpcError) as e:
620 api.WriteFriendReference(
621 references_pb2.WriteFriendReferenceReq(
622 to_user_id=user1.id,
623 text="Another test reference",
624 was_appropriate=True,
625 rating=0.8,
626 )
627 )
628 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
629 assert e.value.details() == "You can only write friend references for confirmed friends."
632def test_host_request_states_references(db, moderator):
633 user1, token1 = generate_user()
634 user2, token2 = generate_user()
636 with session_scope() as session:
637 # can't write ref
638 hr1 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.pending)
639 # can write ref
640 hr2 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.accepted)
641 # can't write ref
642 hr3 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.rejected)
643 # can write ref
644 hr4 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.confirmed)
645 # can't write ref
646 hr5 = create_host_request(session, user2.id, user1.id, timedelta(days=10), status=HostRequestStatus.cancelled)
648 # Approve host requests so both participants can see them
649 moderator.approve_host_request(hr1)
650 moderator.approve_host_request(hr2)
651 moderator.approve_host_request(hr3)
652 moderator.approve_host_request(hr4)
653 moderator.approve_host_request(hr5)
655 with references_session(token1) as api:
656 # pending
657 api.WriteHostRequestReference(
658 references_pb2.WriteHostRequestReferenceReq(
659 host_request_id=hr2,
660 text="Should work!",
661 was_appropriate=True,
662 rating=0.9,
663 )
664 )
666 # accepted
667 api.WriteHostRequestReference(
668 references_pb2.WriteHostRequestReferenceReq(
669 host_request_id=hr4,
670 text="Should work!",
671 was_appropriate=True,
672 rating=0.9,
673 )
674 )
676 # rejected
677 with pytest.raises(grpc.RpcError) as e:
678 api.WriteHostRequestReference(
679 references_pb2.WriteHostRequestReferenceReq(
680 host_request_id=hr1,
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."
689 # confirmed
690 with pytest.raises(grpc.RpcError) as e:
691 api.WriteHostRequestReference(
692 references_pb2.WriteHostRequestReferenceReq(
693 host_request_id=hr3,
694 text="Shouldn't work...",
695 was_appropriate=True,
696 rating=0.9,
697 )
698 )
699 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
700 assert e.value.details() == "You can't write a reference for that host request, or it wasn't found."
702 # cancelled
703 with pytest.raises(grpc.RpcError) as e:
704 api.WriteHostRequestReference(
705 references_pb2.WriteHostRequestReferenceReq(
706 host_request_id=hr5,
707 text="Shouldn't work...",
708 was_appropriate=True,
709 rating=0.9,
710 )
711 )
712 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
713 assert e.value.details() == "You can't write a reference for that host request, or it wasn't found."
716def test_WriteHostRequestReference(db, moderator):
717 user1, token1 = generate_user()
718 user2, token2 = generate_user()
719 user3, token3 = generate_user()
720 user4, token4 = generate_user()
722 with session_scope() as session:
723 # too old
724 hr1 = create_host_request(session, user3.id, user1.id, timedelta(days=20))
725 # valid host req, surfer said we didn't show up but we can still write a req
726 hr2 = create_host_request(session, user3.id, user1.id, timedelta(days=10), surfer_reason_didnt_meetup="No show")
727 # valid surfing req
728 hr3 = create_host_request(session, user1.id, user3.id, timedelta(days=7))
729 # not yet complete
730 hr4 = create_host_request(session, user2.id, user1.id, timedelta(days=1), status=HostRequestStatus.pending)
731 # we indicated we didn't meet
732 hr5 = create_host_request(session, user4.id, user1.id, timedelta(days=7), host_reason_didnt_meetup="")
733 # we will indicate we didn't meet
734 hr6 = create_host_request(session, user4.id, user1.id, timedelta(days=8))
736 # Approve host requests so both participants can see them
737 moderator.approve_host_request(hr1)
738 moderator.approve_host_request(hr2)
739 moderator.approve_host_request(hr3)
740 moderator.approve_host_request(hr4)
741 moderator.approve_host_request(hr5)
742 moderator.approve_host_request(hr6)
744 with references_session(token3) as api:
745 # can write for this one
746 api.WriteHostRequestReference(
747 references_pb2.WriteHostRequestReferenceReq(
748 host_request_id=hr3,
749 text="Should work!",
750 was_appropriate=True,
751 rating=0.9,
752 )
753 )
755 with references_session(token1) as api:
756 # can't write reference for a HR that's not yet finished
757 with pytest.raises(grpc.RpcError) as e:
758 api.WriteHostRequestReference(
759 references_pb2.WriteHostRequestReferenceReq(
760 host_request_id=hr4,
761 text="Shouldn't work...",
762 was_appropriate=True,
763 rating=0.9,
764 )
765 )
766 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
767 assert e.value.details() == "You can't write a reference for that host request, or it wasn't found."
769 # can't write reference that's more than 2 weeks old
770 with pytest.raises(grpc.RpcError) as e:
771 api.WriteHostRequestReference(
772 references_pb2.WriteHostRequestReferenceReq(
773 host_request_id=hr1,
774 text="Shouldn't work...",
775 was_appropriate=True,
776 rating=0.9,
777 )
778 )
779 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
780 assert e.value.details() == "You can't write a reference for that host request, or it wasn't found."
782 # can write for this one
783 api.WriteHostRequestReference(
784 references_pb2.WriteHostRequestReferenceReq(
785 host_request_id=hr2,
786 text="Should work!",
787 was_appropriate=True,
788 rating=0.9,
789 )
790 )
792 # but can't write a second one for the same one
793 with pytest.raises(grpc.RpcError) as e:
794 api.WriteHostRequestReference(
795 references_pb2.WriteHostRequestReferenceReq(
796 host_request_id=hr2,
797 text="Shouldn't work...",
798 was_appropriate=True,
799 rating=0.9,
800 )
801 )
802 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
803 assert e.value.details() == "Reference already given."
805 # can write for this one too
806 api.WriteHostRequestReference(
807 references_pb2.WriteHostRequestReferenceReq(
808 host_request_id=hr3,
809 text="Should work!",
810 was_appropriate=True,
811 rating=0.9,
812 )
813 )
815 # can't write reference for a HR that we indicated we didn't show up
816 with pytest.raises(grpc.RpcError) as e:
817 api.WriteHostRequestReference(
818 references_pb2.WriteHostRequestReferenceReq(
819 host_request_id=hr5,
820 text="Shouldn't work...",
821 was_appropriate=True,
822 rating=0.9,
823 )
824 )
825 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
826 assert (
827 e.value.details()
828 == "You can't write a reference for that host request because you indicated that you didn't meet up."
829 )
831 # can't write reference for a HR that we indicate we didn't show up for
832 api.HostRequestIndicateDidntMeetup(
833 references_pb2.HostRequestIndicateDidntMeetupReq(
834 host_request_id=hr6,
835 reason_didnt_meetup="No clue?",
836 )
837 )
839 with pytest.raises(grpc.RpcError) as e:
840 api.WriteHostRequestReference(
841 references_pb2.WriteHostRequestReferenceReq(
842 host_request_id=hr6,
843 text="Shouldn't work...",
844 was_appropriate=True,
845 rating=0.9,
846 )
847 )
848 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
849 assert (
850 e.value.details()
851 == "You can't write a reference for that host request because you indicated that you didn't meet up."
852 )
854 with references_session(token4) as api:
855 # they can still write one
856 api.WriteHostRequestReference(
857 references_pb2.WriteHostRequestReferenceReq(
858 host_request_id=hr6,
859 text="Should work!",
860 was_appropriate=True,
861 rating=0.9,
862 )
863 )
866def test_WriteHostRequestReference_private_text(
867 db, email_collector: EmailCollector, push_collector: PushCollector, moderator
868):
869 user1, token1 = generate_user()
870 user2, token2 = generate_user()
872 with session_scope() as session:
873 hr = create_host_request(session, user1.id, user2.id, timedelta(days=10))
874 moderator.approve_host_request(hr)
876 with references_session(token1) as api:
877 ref = api.WriteHostRequestReference(
878 references_pb2.WriteHostRequestReferenceReq(
879 host_request_id=hr,
880 text="Should work!",
881 was_appropriate=True,
882 rating=0.9,
883 private_text="Something",
884 )
885 )
886 # Approve before patches/jobs exit so the pending notification is delivered while mocked.
887 moderator.approve_reference(ref.reference_id)
889 # make sure an email was sent to the user receiving the ref as well as the mods
890 email_collector.pop_for_reports(last=True)
891 email = email_collector.pop_for_recipient(user2.email, last=True)
892 assert email.subject == f"[TEST] You've received a reference from {user1.name}!"
893 assert email.recipient == user2.email
895 push = push_collector.pop_for_user(user2.id, last=True)
896 assert push.content.title == f"New reference from {user1.name}"
897 assert push.content.body == f"{user1.name} left you a reference, now it's your turn to write theirs!"
900def test_GetHostRequestReferenceStatus(db, moderator):
901 user1, token1 = generate_user()
902 user2, token2 = generate_user()
904 # user1 writes; RPC returns has_given True
905 with session_scope() as session:
906 hr1 = create_host_request(session, user1.id, user2.id, timedelta(days=7))
907 moderator.approve_host_request(hr1)
908 with references_session(token1) as api:
909 api.WriteHostRequestReference(
910 references_pb2.WriteHostRequestReferenceReq(
911 host_request_id=hr1, text="Great stay!", was_appropriate=True, rating=0.9
912 )
913 )
914 res = api.GetHostRequestReferenceStatus(references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr1))
915 assert res.has_given is True
917 # false: no reference written yet
918 with session_scope() as session:
919 hr2 = create_host_request(session, user1.id, user2.id, timedelta(days=7))
920 moderator.approve_host_request(hr2)
921 with references_session(token1) as api:
922 res = api.GetHostRequestReferenceStatus(references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr2))
923 assert res.has_given is False
925 # false: other user wrote a reference
926 with session_scope() as session:
927 hr3 = create_host_request(session, user1.id, user2.id, timedelta(days=7))
928 moderator.approve_host_request(hr3)
929 with references_session(token2) as api:
930 api.WriteHostRequestReference(
931 references_pb2.WriteHostRequestReferenceReq(
932 host_request_id=hr3, text="Lovely guest!", was_appropriate=True, rating=0.95
933 )
934 )
935 with references_session(token1) as api:
936 res = api.GetHostRequestReferenceStatus(references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr3))
937 assert res.has_given is False
939 # false: nonexistent host request id
940 with references_session(token1) as api:
941 res = api.GetHostRequestReferenceStatus(references_pb2.GetHostRequestReferenceStatusReq(host_request_id=999999))
942 assert res.has_given is False
944 # Additional status flags
945 with session_scope() as session:
946 # expired (too old)
947 hr_expired = create_host_request(session, user2.id, user1.id, timedelta(days=20))
948 # current user (host) indicated didn't meet up
949 hr_didnt_stay_host = create_host_request(
950 session, user2.id, user1.id, timedelta(days=10), host_reason_didnt_meetup=""
951 )
952 # other user (surfer) indicated didn't meet up
953 hr_other_didnt_stay = create_host_request(
954 session, user2.id, user1.id, timedelta(days=10), surfer_reason_didnt_meetup="No show"
955 )
957 moderator.approve_host_request(hr_expired)
958 moderator.approve_host_request(hr_didnt_stay_host)
959 moderator.approve_host_request(hr_other_didnt_stay)
961 # expired: is_expired true, can_write false, didnt_stay false
962 with references_session(token1) as api:
963 res = api.GetHostRequestReferenceStatus(
964 references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr_expired)
965 )
966 assert res.has_given is False
967 assert res.is_expired is True
968 assert res.can_write is False
969 assert res.didnt_stay is False
971 # current user indicated didn't meet up: didnt_stay true, can_write false, not expired
972 with references_session(token1) as api:
973 res = api.GetHostRequestReferenceStatus(
974 references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr_didnt_stay_host)
975 )
976 assert res.has_given is False
977 assert res.is_expired is False
978 assert res.didnt_stay is True
979 assert res.can_write is False
981 # other party indicated didn't meet up: didnt_stay false, can_write true (within window), not expired
982 with references_session(token1) as api:
983 res = api.GetHostRequestReferenceStatus(
984 references_pb2.GetHostRequestReferenceStatusReq(host_request_id=hr_other_didnt_stay)
985 )
986 assert res.has_given is False
987 assert res.is_expired is False
988 assert res.didnt_stay is False
989 assert res.can_write is True
992def test_AvailableWriteReferences_and_ListPendingReferencesToWrite(db, moderator):
993 user1, token1 = generate_user()
994 user2, token2 = generate_user()
995 user3, token3 = generate_user()
996 user4, token4 = generate_user()
997 user5, token5 = generate_user(delete_user=True)
998 user6, token6 = generate_user()
999 user7, token7 = generate_user()
1000 user8, token8 = generate_user()
1001 user9, token9 = generate_user()
1002 user10, token10 = generate_user()
1003 user11, token11 = generate_user()
1004 make_user_block(user1, user6)
1005 make_user_block(user7, user1)
1007 with session_scope() as session:
1008 # too old
1009 hr1 = create_host_request(session, user3.id, user1.id, timedelta(days=20))
1011 # already wrote friend ref to user3
1012 create_friend_reference(session, user1.id, user3.id, timedelta(days=15, seconds=70))
1014 # already given
1015 _, hr2 = create_host_reference(session, user2.id, user1.id, timedelta(days=10, seconds=110), surfing=True)
1016 create_host_reference(session, user1.id, user2.id, timedelta(days=10, seconds=100), host_request_id=hr2)
1018 # valid hosted
1019 hr3 = create_host_request(session, user3.id, user1.id, timedelta(days=8))
1021 # valid surfed
1022 hr4 = create_host_request(session, user1.id, user4.id, timedelta(days=5))
1024 # not yet complete
1025 hr5 = create_host_request(session, user2.id, user1.id, timedelta(days=2), status=HostRequestStatus.pending)
1027 # already wrote friend ref to user2
1028 create_friend_reference(session, user1.id, user2.id, timedelta(days=1))
1030 # user5 deleted, reference won't show up as pending
1031 hr_user5 = create_host_request(session, user1.id, user5.id, timedelta(days=5))
1033 # user6 blocked, reference won't show up as pending
1034 hr_user6 = create_host_request(session, user1.id, user6.id, timedelta(days=5))
1036 # user7 blocking, reference won't show up as pending
1037 hr_user7 = create_host_request(session, user1.id, user7.id, timedelta(days=5))
1039 # hosted but we indicated we didn't meet up, no reason; should not show up
1040 hr_user8 = create_host_request(session, user8.id, user1.id, timedelta(days=11), host_reason_didnt_meetup="")
1042 # surfed but we indicated we didn't meet up, has reason; should not show up
1043 hr_user9 = create_host_request(
1044 session, user1.id, user9.id, timedelta(days=10), surfer_reason_didnt_meetup="They never showed up!"
1045 )
1047 # surfed but they indicated we didn't meet up, no reason; should show up
1048 hr6 = create_host_request(session, user1.id, user10.id, timedelta(days=4), host_reason_didnt_meetup="")
1050 # hosted but they indicated we didn't meet up, has reason; should show up
1051 hr7 = create_host_request(
1052 session, user11.id, user1.id, timedelta(days=3), surfer_reason_didnt_meetup="They never showed up!!"
1053 )
1055 # Approve all host requests so both participants can see them
1056 moderator.approve_host_request(hr1)
1057 moderator.approve_host_request(hr2)
1058 moderator.approve_host_request(hr3)
1059 moderator.approve_host_request(hr4)
1060 moderator.approve_host_request(hr5)
1061 moderator.approve_host_request(hr_user5)
1062 moderator.approve_host_request(hr_user6)
1063 moderator.approve_host_request(hr_user7)
1064 moderator.approve_host_request(hr_user8)
1065 moderator.approve_host_request(hr_user9)
1066 moderator.approve_host_request(hr6)
1067 moderator.approve_host_request(hr7)
1069 refresh_materialized_views_rapid(empty_pb2.Empty())
1071 with references_session(token1) as api:
1072 # can't write reference for invisible user
1073 with pytest.raises(grpc.RpcError) as e:
1074 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user5.id))
1075 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1076 assert e.value.details() == "Couldn't find that user."
1078 # can't write reference for blocking user
1079 with pytest.raises(grpc.RpcError) as e:
1080 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user7.id))
1081 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1082 assert e.value.details() == "Couldn't find that user."
1084 # can't write reference for blocked user
1085 with pytest.raises(grpc.RpcError) as e:
1086 api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user6.id))
1087 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1088 assert e.value.details() == "Couldn't find that user."
1090 # can't write anything to myself
1091 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
1092 assert not res.can_write_friend_reference
1093 assert len(res.available_write_references) == 0
1095 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
1096 # can't write friend ref to user2
1097 assert not res.can_write_friend_reference
1098 # none we can write for user2
1099 assert len(res.available_write_references) == 0
1101 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user3.id))
1102 # can't write friend ref to user3
1103 assert not res.can_write_friend_reference
1104 # can write one reference because we hosted user3
1105 assert len(res.available_write_references) == 1
1106 w = res.available_write_references[0]
1107 assert w.host_request_id == hr3
1108 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1109 assert now() + timedelta(days=6) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=7)
1111 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user4.id))
1112 # can write friend ref to user4
1113 assert res.can_write_friend_reference
1114 # can write one reference because we surfed with user4
1115 assert len(res.available_write_references) == 1
1116 w = res.available_write_references[0]
1117 assert w.host_request_id == hr4
1118 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
1119 assert now() + timedelta(days=9) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=10)
1121 # can't write a req if we indicated we didn't meet up
1122 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user8.id))
1123 assert len(res.available_write_references) == 0
1124 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user9.id))
1125 assert len(res.available_write_references) == 0
1127 # can still write ref if the other person indicated we didn't meet up
1128 # surfed with them
1129 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user10.id))
1130 assert len(res.available_write_references) == 1
1131 w = res.available_write_references[0]
1132 assert w.host_request_id == hr6
1133 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
1134 assert now() + timedelta(days=10) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=11)
1135 # hosted them
1136 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user11.id))
1137 assert len(res.available_write_references) == 1
1138 w = res.available_write_references[0]
1139 assert w.host_request_id == hr7
1140 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1141 assert now() + timedelta(days=11) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=12)
1143 # finally check the general list
1144 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1145 assert len(res.pending_references) == 4
1146 w = res.pending_references[0]
1147 assert w.host_request_id == hr3
1148 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1149 assert now() + timedelta(days=6) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=7)
1150 w = res.pending_references[1]
1151 assert w.host_request_id == hr4
1152 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
1153 assert now() + timedelta(days=9) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=10)
1154 w = res.pending_references[2]
1155 assert w.host_request_id == hr6
1156 assert w.reference_type == references_pb2.REFERENCE_TYPE_SURFED
1157 assert now() + timedelta(days=10) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=11)
1158 w = res.pending_references[3]
1159 assert w.host_request_id == hr7
1160 assert w.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1161 assert now() + timedelta(days=11) <= to_aware_datetime(w.time_expires) <= now() + timedelta(days=12)
1163 with account_session(token1) as account:
1164 reminders = account.GetReminders(empty_pb2.Empty()).reminders
1165 assert [reminder.WhichOneof("reminder") for reminder in reminders] == [
1166 "write_reference_reminder",
1167 "write_reference_reminder",
1168 "write_reference_reminder",
1169 "write_reference_reminder",
1170 ]
1171 assert reminders[0].write_reference_reminder.host_request_id == hr3
1172 assert reminders[0].write_reference_reminder.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1173 assert reminders[0].write_reference_reminder.other_user.user_id == user3.id
1174 assert reminders[1].write_reference_reminder.host_request_id == hr4
1175 assert reminders[1].write_reference_reminder.reference_type == references_pb2.REFERENCE_TYPE_SURFED
1176 assert reminders[1].write_reference_reminder.other_user.user_id == user4.id
1177 assert reminders[2].write_reference_reminder.host_request_id == hr6
1178 assert reminders[2].write_reference_reminder.reference_type == references_pb2.REFERENCE_TYPE_SURFED
1179 assert reminders[2].write_reference_reminder.other_user.user_id == user10.id
1180 assert reminders[3].write_reference_reminder.host_request_id == hr7
1181 assert reminders[3].write_reference_reminder.reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1182 assert reminders[3].write_reference_reminder.other_user.user_id == user11.id
1185@pytest.mark.parametrize("hs", ["host", "surfer"])
1186def test_regression_disappearing_refs(db, hs, moderator):
1187 """
1188 Roughly the reproduction steps are:
1189 * Send a host request, then have both host and surfer accept
1190 * Wait for it to elapse (or hack it with SQL like what you told me to do)
1191 * On the surfer account, leave a reference
1192 * Then on the host account, the option to leave a reference is then not available
1193 """
1194 user1, token1 = generate_user()
1195 user2, token2 = generate_user()
1196 req_start = (today() + timedelta(days=2)).isoformat()
1197 req_end = (today() + timedelta(days=3)).isoformat()
1198 with requests_session(token1) as api:
1199 res = api.CreateHostRequest(
1200 requests_pb2.CreateHostRequestReq(
1201 host_user_id=user2.id, from_date=req_start, to_date=req_end, text=valid_request_text()
1202 )
1203 )
1204 host_request_id = res.host_request_id
1206 moderator.approve_host_request(host_request_id)
1208 assert (
1209 api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True))
1210 .host_requests[0]
1211 .latest_message.text.text
1212 == valid_request_text()
1213 )
1215 with requests_session(token2) as api:
1216 api.RespondHostRequest(
1217 requests_pb2.RespondHostRequestReq(
1218 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
1219 )
1220 )
1222 with requests_session(token1) as api:
1223 api.RespondHostRequest(
1224 requests_pb2.RespondHostRequestReq(
1225 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
1226 )
1227 )
1229 refresh_materialized_views_rapid(empty_pb2.Empty())
1231 with references_session(token1) as api:
1232 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1233 assert len(res.pending_references) == 0
1234 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
1235 assert len(res.available_write_references) == 0
1237 with references_session(token2) as api:
1238 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1239 assert len(res.pending_references) == 0
1240 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
1241 assert len(res.available_write_references) == 0
1243 # hack the time backwards
1244 hack_req_start = today() - timedelta(days=10) + timedelta(days=2)
1245 hack_req_end = today() - timedelta(days=10) + timedelta(days=3)
1246 with session_scope() as session:
1247 host_request = session.execute(select(HostRequest)).scalar_one()
1248 assert host_request.conversation_id == host_request_id
1249 host_request.from_date = hack_req_start
1250 host_request.to_date = hack_req_end
1252 with references_session(token1) as api:
1253 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1254 assert len(res.pending_references) == 1
1255 assert res.pending_references[0].host_request_id == host_request_id
1256 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
1258 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
1259 assert len(res.available_write_references) == 1
1260 assert res.available_write_references[0].host_request_id == host_request_id
1261 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
1263 with references_session(token2) as api:
1264 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1265 assert len(res.pending_references) == 1
1266 assert res.pending_references[0].host_request_id == host_request_id
1267 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1269 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
1270 assert len(res.available_write_references) == 1
1271 assert res.available_write_references[0].host_request_id == host_request_id
1272 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1274 if hs == "host":
1275 with references_session(token2) as api:
1276 api.WriteHostRequestReference(
1277 references_pb2.WriteHostRequestReferenceReq(
1278 host_request_id=host_request_id,
1279 text="Good stuff",
1280 was_appropriate=True,
1281 rating=0.86,
1282 )
1283 )
1285 with references_session(token2) as api:
1286 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1287 assert len(res.pending_references) == 0
1289 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
1290 assert len(res.available_write_references) == 0
1292 with references_session(token1) as api:
1293 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1294 assert len(res.pending_references) == 1
1295 assert res.pending_references[0].host_request_id == host_request_id
1296 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
1298 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user2.id))
1299 assert len(res.available_write_references) == 1
1300 assert res.available_write_references[0].host_request_id == host_request_id
1301 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_SURFED
1302 else:
1303 with references_session(token1) as api:
1304 api.WriteHostRequestReference(
1305 references_pb2.WriteHostRequestReferenceReq(
1306 host_request_id=host_request_id,
1307 text="Good stuff",
1308 was_appropriate=True,
1309 rating=0.86,
1310 )
1311 )
1313 with references_session(token1) as api:
1314 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1315 assert len(res.pending_references) == 0
1317 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
1318 assert len(res.available_write_references) == 0
1320 with references_session(token2) as api:
1321 res = api.ListPendingReferencesToWrite(empty_pb2.Empty())
1322 assert len(res.pending_references) == 1
1323 assert res.pending_references[0].host_request_id == host_request_id
1324 assert res.pending_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1326 res = api.AvailableWriteReferences(references_pb2.AvailableWriteReferencesReq(to_user_id=user1.id))
1327 assert len(res.available_write_references) == 1
1328 assert res.available_write_references[0].host_request_id == host_request_id
1329 assert res.available_write_references[0].reference_type == references_pb2.REFERENCE_TYPE_HOSTED
1332def test_WriteFriendReference_creates_shadowed_moderation_state(db):
1333 """New friend references start out shadowed and are not visible to non-authors."""
1334 user1, token1 = generate_user()
1335 user2, token2 = generate_user()
1336 user3, token3 = generate_user()
1337 make_friends(user1, user2)
1339 with references_session(token1) as api:
1340 ref = api.WriteFriendReference(
1341 references_pb2.WriteFriendReferenceReq(
1342 to_user_id=user2.id, text="Nice friend", was_appropriate=True, rating=0.9
1343 )
1344 )
1346 with session_scope() as session:
1347 reference = session.execute(select(Reference).where(Reference.id == ref.reference_id)).scalar_one()
1348 assert reference.moderation_state.visibility == ModerationVisibility.shadowed
1349 assert reference.moderation_state.object_type == ModerationObjectType.reference
1350 assert reference.moderation_state.object_id == reference.id
1352 # Author can see their own shadowed reference.
1353 with references_session(token1) as api:
1354 res = api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id))
1355 assert [r.reference_id for r in res.references] == [ref.reference_id]
1357 # Non-author user cannot see it while shadowed.
1358 with references_session(token3) as api:
1359 res = api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user2.id))
1360 assert [r.reference_id for r in res.references] == []
1363def test_reference_hidden_via_ums_disappears_from_listings(db, moderator):
1364 """Hiding a reference through UMS removes it from listings, including for the author."""
1365 user1, token1 = generate_user()
1366 user2, token2 = generate_user()
1367 user3, token3 = generate_user()
1368 make_friends(user1, user2)
1370 with references_session(token1) as api:
1371 ref = api.WriteFriendReference(
1372 references_pb2.WriteFriendReferenceReq(
1373 to_user_id=user2.id, text="Visible for now", was_appropriate=True, rating=0.9
1374 )
1375 )
1376 moderator.approve_reference(ref.reference_id)
1378 with references_session(token3) as api:
1379 assert api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user2.id)).references
1381 # Hide the reference via the moderation API.
1382 with real_moderation_session(moderator.token) as mod_api:
1383 state_res = mod_api.GetModerationState(
1384 moderation_pb2.GetModerationStateReq(
1385 object_type=moderation_pb2.MODERATION_OBJECT_TYPE_REFERENCE,
1386 object_id=ref.reference_id,
1387 )
1388 )
1389 mod_api.ModerateContent(
1390 moderation_pb2.ModerateContentReq(
1391 moderation_state_id=state_res.moderation_state.moderation_state_id,
1392 action=moderation_pb2.MODERATION_ACTION_HIDE,
1393 visibility=moderation_pb2.MODERATION_VISIBILITY_HIDDEN,
1394 reason="test",
1395 )
1396 )
1398 # Hidden references are invisible to the author and to other users.
1399 with references_session(token1) as api:
1400 assert not api.ListReferences(references_pb2.ListReferencesReq(from_user_id=user1.id)).references
1401 with references_session(token3) as api:
1402 assert not api.ListReferences(references_pb2.ListReferencesReq(to_user_id=user2.id)).references