Coverage for src/tests/test_requests.py: 99%
676 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-10 02:35 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-10 02:35 +0000
1import re
2from datetime import timedelta
3from urllib.parse import parse_qs, urlparse
5import grpc
6import pytest
7from sqlalchemy.sql import select
9from couchers.crypto import b64decode
10from couchers.db import session_scope
11from couchers.materialized_views import refresh_materialized_view
12from couchers.models import (
13 Message,
14 MessageType,
15 RateLimitAction,
16)
17from couchers.rate_limits.definitions import RATE_LIMIT_DEFINITIONS, RATE_LIMIT_INTERVAL_STRING
18from couchers.sql import couchers_select as select
19from couchers.templates.v2 import v2date
20from couchers.utils import create_coordinate, now, today
21from proto import (
22 api_pb2,
23 auth_pb2,
24 conversations_pb2,
25 requests_pb2,
26)
27from proto.internal import unsubscribe_pb2
28from tests.test_fixtures import ( # noqa
29 api_session,
30 auth_api_session,
31 db,
32 email_fields,
33 generate_user,
34 mock_notification_email,
35 push_collector,
36 requests_session,
37 testconfig,
38)
41@pytest.fixture(autouse=True)
42def _(testconfig):
43 pass
46def test_create_request(db):
47 user1, token1 = generate_user()
48 hosting_city = "Morningside Heights, New York City"
49 hosting_lat = 40.8086
50 hosting_lng = -73.9616
51 hosting_radius = 500
52 user2, token2 = generate_user(
53 city=hosting_city,
54 geom=create_coordinate(hosting_lat, hosting_lng),
55 geom_radius=hosting_radius,
56 )
58 today_plus_2 = (today() + timedelta(days=2)).isoformat()
59 today_plus_3 = (today() + timedelta(days=3)).isoformat()
60 today_minus_2 = (today() - timedelta(days=2)).isoformat()
61 today_minus_3 = (today() - timedelta(days=3)).isoformat()
62 with requests_session(token1) as api:
63 with pytest.raises(grpc.RpcError) as e:
64 api.CreateHostRequest(
65 requests_pb2.CreateHostRequestReq(
66 host_user_id=user1.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
67 )
68 )
69 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
70 assert e.value.details() == "Can't request hosting from yourself."
72 with pytest.raises(grpc.RpcError) as e:
73 api.CreateHostRequest(
74 requests_pb2.CreateHostRequestReq(
75 host_user_id=999, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
76 )
77 )
78 assert e.value.code() == grpc.StatusCode.NOT_FOUND
79 assert e.value.details() == "Couldn't find that user."
81 with pytest.raises(grpc.RpcError) as e:
82 api.CreateHostRequest(
83 requests_pb2.CreateHostRequestReq(
84 host_user_id=user2.id, from_date=today_plus_3, to_date=today_plus_2, text="Test request"
85 )
86 )
87 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
88 assert e.value.details() == "From date can't be after to date."
90 with pytest.raises(grpc.RpcError) as e:
91 api.CreateHostRequest(
92 requests_pb2.CreateHostRequestReq(
93 host_user_id=user2.id, from_date=today_minus_3, to_date=today_plus_2, text="Test request"
94 )
95 )
96 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
97 assert e.value.details() == "From date must be today or later."
99 with pytest.raises(grpc.RpcError) as e:
100 api.CreateHostRequest(
101 requests_pb2.CreateHostRequestReq(
102 host_user_id=user2.id, from_date=today_plus_2, to_date=today_minus_2, text="Test request"
103 )
104 )
105 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
106 assert e.value.details() == "From date can't be after to date."
108 with pytest.raises(grpc.RpcError) as e:
109 api.CreateHostRequest(
110 requests_pb2.CreateHostRequestReq(
111 host_user_id=user2.id, from_date="2020-00-06", to_date=today_minus_2, text="Test request"
112 )
113 )
114 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
115 assert e.value.details() == "Invalid date."
117 res = api.CreateHostRequest(
118 requests_pb2.CreateHostRequestReq(
119 host_user_id=user2.id,
120 from_date=today_plus_2,
121 to_date=today_plus_3,
122 text="Test request",
123 )
124 )
126 host_requests = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True)).host_requests
128 assert len(host_requests) == 1
129 hr = host_requests[0]
131 assert hr.latest_message.text.text == "Test request"
133 assert hr.hosting_city == hosting_city
134 assert round(hr.hosting_lat, 4) == hosting_lat
135 assert round(hr.hosting_lng, 4) == hosting_lng
136 assert hr.hosting_radius == hosting_radius
138 today_ = today()
139 today_plus_one_year = today_ + timedelta(days=365)
140 today_plus_one_year_plus_2 = (today_plus_one_year + timedelta(days=2)).isoformat()
141 today_plus_one_year_plus_3 = (today_plus_one_year + timedelta(days=3)).isoformat()
142 with pytest.raises(grpc.RpcError) as e:
143 api.CreateHostRequest(
144 requests_pb2.CreateHostRequestReq(
145 host_user_id=user2.id,
146 from_date=today_plus_one_year_plus_2,
147 to_date=today_plus_one_year_plus_3,
148 text="Test from date after one year",
149 )
150 )
151 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
152 assert e.value.details() == "The start date must be within one year from today."
154 with pytest.raises(grpc.RpcError) as e:
155 api.CreateHostRequest(
156 requests_pb2.CreateHostRequestReq(
157 host_user_id=user2.id,
158 from_date=today_plus_2,
159 to_date=today_plus_one_year_plus_3,
160 text="Test to date one year after from date",
161 )
162 )
163 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
164 assert e.value.details() == "You cannot request to stay with someone for longer than one year."
167def test_create_request_incomplete_profile(db):
168 user1, token1 = generate_user(complete_profile=False)
169 user2, _ = generate_user()
170 today_plus_2 = (today() + timedelta(days=2)).isoformat()
171 today_plus_3 = (today() + timedelta(days=3)).isoformat()
172 with requests_session(token1) as api:
173 with pytest.raises(grpc.RpcError) as e:
174 api.CreateHostRequest(
175 requests_pb2.CreateHostRequestReq(
176 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
177 )
178 )
179 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
180 assert e.value.details() == "You have to complete your profile before you can send a request."
183def test_excessive_requests_are_reported(db):
184 """Test that excessive host requests are first reported in a warning email and finally lead blocking of further requests."""
185 user, token = generate_user()
186 today_plus_2 = (today() + timedelta(days=2)).isoformat()
187 today_plus_3 = (today() + timedelta(days=3)).isoformat()
188 rate_limit_definition = RATE_LIMIT_DEFINITIONS[RateLimitAction.host_request]
189 with requests_session(token) as api:
190 # Test warning email
191 with mock_notification_email() as mock_email:
192 for _ in range(rate_limit_definition.warning_limit):
193 host_user, _ = generate_user()
194 _ = api.CreateHostRequest(
195 requests_pb2.CreateHostRequestReq(
196 host_user_id=host_user.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
197 )
198 )
200 assert mock_email.call_count == 0
201 host_user, _ = generate_user()
202 _ = api.CreateHostRequest(
203 requests_pb2.CreateHostRequestReq(
204 host_user_id=host_user.id,
205 from_date=today_plus_2,
206 to_date=today_plus_3,
207 text="Excessive test request",
208 )
209 )
210 assert mock_email.call_count == 1
211 email = mock_email.mock_calls[0].kwargs["plain"]
212 assert email.startswith(
213 f"User {user.username} has sent {rate_limit_definition.warning_limit} host requests in the past {RATE_LIMIT_INTERVAL_STRING}."
214 )
216 # Test ban after exceeding HOST_REQUEST_HARD_LIMIT
217 with mock_notification_email() as mock_email:
218 for _ in range(rate_limit_definition.hard_limit - rate_limit_definition.warning_limit - 1):
219 host_user, _ = generate_user()
220 _ = api.CreateHostRequest(
221 requests_pb2.CreateHostRequestReq(
222 host_user_id=host_user.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
223 )
224 )
226 assert mock_email.call_count == 0
227 host_user, _ = generate_user()
228 with pytest.raises(grpc.RpcError) as exc_info:
229 _ = api.CreateHostRequest(
230 requests_pb2.CreateHostRequestReq(
231 host_user_id=host_user.id,
232 from_date=today_plus_2,
233 to_date=today_plus_3,
234 text="Excessive test request",
235 )
236 )
237 assert exc_info.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED
238 assert (
239 exc_info.value.details()
240 == "You have sent a lot of host requests in the past 24 hours. To avoid spam, you can't send any more for now."
241 )
243 assert mock_email.call_count == 1
244 email = mock_email.mock_calls[0].kwargs["plain"]
245 assert email.startswith(
246 f"User {user.username} has sent {rate_limit_definition.hard_limit} host requests in the past {RATE_LIMIT_INTERVAL_STRING}."
247 )
248 assert "The user has been blocked from sending further host requests for now." in email
251def add_message(db, text, author_id, conversation_id):
252 with session_scope() as session:
253 message = Message(
254 conversation_id=conversation_id, author_id=author_id, text=text, message_type=MessageType.text
255 )
257 session.add(message)
260def test_GetHostRequest(db):
261 user1, token1 = generate_user()
262 user2, token2 = generate_user()
263 user3, token3 = generate_user()
264 today_plus_2 = (today() + timedelta(days=2)).isoformat()
265 today_plus_3 = (today() + timedelta(days=3)).isoformat()
266 with requests_session(token1) as api:
267 host_request_id = api.CreateHostRequest(
268 requests_pb2.CreateHostRequestReq(
269 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
270 )
271 ).host_request_id
273 with pytest.raises(grpc.RpcError) as e:
274 api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=999))
275 assert e.value.code() == grpc.StatusCode.NOT_FOUND
276 assert e.value.details() == "Couldn't find that host request."
278 api.SendHostRequestMessage(
279 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
280 )
282 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=host_request_id))
283 assert res.latest_message.text.text == "Test message 1"
286def test_ListHostRequests(db):
287 user1, token1 = generate_user()
288 user2, token2 = generate_user()
289 user3, token3 = generate_user()
290 today_plus_2 = (today() + timedelta(days=2)).isoformat()
291 today_plus_3 = (today() + timedelta(days=3)).isoformat()
292 with requests_session(token1) as api:
293 host_request_1 = api.CreateHostRequest(
294 requests_pb2.CreateHostRequestReq(
295 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
296 )
297 ).host_request_id
299 host_request_2 = api.CreateHostRequest(
300 requests_pb2.CreateHostRequestReq(
301 host_user_id=user3.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 2"
302 )
303 ).host_request_id
305 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True))
306 assert res.no_more
307 assert len(res.host_requests) == 2
309 with requests_session(token2) as api:
310 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
311 assert res.no_more
312 assert len(res.host_requests) == 1
313 assert res.host_requests[0].latest_message.text.text == "Test request 1"
314 assert res.host_requests[0].surfer_user_id == user1.id
315 assert res.host_requests[0].host_user_id == user2.id
316 assert res.host_requests[0].status == conversations_pb2.HOST_REQUEST_STATUS_PENDING
318 add_message(db, "Test request 1 message 1", user2.id, host_request_1)
319 add_message(db, "Test request 1 message 2", user2.id, host_request_1)
320 add_message(db, "Test request 1 message 3", user2.id, host_request_1)
322 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
323 assert res.host_requests[0].latest_message.text.text == "Test request 1 message 3"
325 api.CreateHostRequest(
326 requests_pb2.CreateHostRequestReq(
327 host_user_id=user1.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 3"
328 )
329 )
331 add_message(db, "Test request 2 message 1", user1.id, host_request_2)
332 add_message(db, "Test request 2 message 2", user3.id, host_request_2)
334 with requests_session(token3) as api:
335 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
336 assert res.no_more
337 assert len(res.host_requests) == 1
338 assert res.host_requests[0].latest_message.text.text == "Test request 2 message 2"
340 with requests_session(token1) as api:
341 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
342 assert len(res.host_requests) == 1
344 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq())
345 assert len(res.host_requests) == 3
348def test_ListHostRequests_pagination_regression(db):
349 """
350 ListHostRequests was skipping a request when getting multiple pages
351 """
352 user1, token1 = generate_user()
353 user2, token2 = generate_user()
354 today_plus_2 = (today() + timedelta(days=2)).isoformat()
355 today_plus_3 = (today() + timedelta(days=3)).isoformat()
356 with requests_session(token1) as api:
357 host_request_1 = api.CreateHostRequest(
358 requests_pb2.CreateHostRequestReq(
359 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
360 )
361 ).host_request_id
363 host_request_2 = api.CreateHostRequest(
364 requests_pb2.CreateHostRequestReq(
365 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 2"
366 )
367 ).host_request_id
369 host_request_3 = api.CreateHostRequest(
370 requests_pb2.CreateHostRequestReq(
371 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 3"
372 )
373 ).host_request_id
375 with requests_session(token2) as api:
376 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
377 assert res.no_more
378 assert len(res.host_requests) == 3
379 assert res.host_requests[0].latest_message.text.text == "Test request 3"
380 assert res.host_requests[1].latest_message.text.text == "Test request 2"
381 assert res.host_requests[2].latest_message.text.text == "Test request 1"
383 with requests_session(token2) as api:
384 api.RespondHostRequest(
385 requests_pb2.RespondHostRequestReq(
386 host_request_id=host_request_2,
387 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
388 text="Accepting host request 2",
389 )
390 )
391 api.RespondHostRequest(
392 requests_pb2.RespondHostRequestReq(
393 host_request_id=host_request_1,
394 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
395 text="Accepting host request 1",
396 )
397 )
398 api.RespondHostRequest(
399 requests_pb2.RespondHostRequestReq(
400 host_request_id=host_request_3,
401 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
402 text="Accepting host request 3",
403 )
404 )
406 with requests_session(token2) as api:
407 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
408 assert res.no_more
409 assert len(res.host_requests) == 3
410 assert res.host_requests[0].latest_message.text.text == "Accepting host request 3"
411 assert res.host_requests[1].latest_message.text.text == "Accepting host request 1"
412 assert res.host_requests[2].latest_message.text.text == "Accepting host request 2"
414 with requests_session(token2) as api:
415 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True, number=1))
416 assert not res.no_more
417 assert len(res.host_requests) == 1
418 assert res.host_requests[0].latest_message.text.text == "Accepting host request 3"
419 res = api.ListHostRequests(
420 requests_pb2.ListHostRequestsReq(only_received=True, number=1, last_request_id=res.last_request_id)
421 )
422 assert not res.no_more
423 assert len(res.host_requests) == 1
424 assert res.host_requests[0].latest_message.text.text == "Accepting host request 1"
425 res = api.ListHostRequests(
426 requests_pb2.ListHostRequestsReq(only_received=True, number=1, last_request_id=res.last_request_id)
427 )
428 assert res.no_more
429 assert len(res.host_requests) == 1
430 assert res.host_requests[0].latest_message.text.text == "Accepting host request 2"
433def test_ListHostRequests_active_filter(db):
434 user1, token1 = generate_user()
435 user2, token2 = generate_user()
436 today_plus_2 = (today() + timedelta(days=2)).isoformat()
437 today_plus_3 = (today() + timedelta(days=3)).isoformat()
439 with requests_session(token1) as api:
440 request_id = api.CreateHostRequest(
441 requests_pb2.CreateHostRequestReq(
442 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
443 )
444 ).host_request_id
445 api.RespondHostRequest(
446 requests_pb2.RespondHostRequestReq(
447 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
448 )
449 )
451 with requests_session(token2) as api:
452 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
453 assert len(res.host_requests) == 1
454 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_active=True))
455 assert len(res.host_requests) == 0
458def test_RespondHostRequests(db):
459 user1, token1 = generate_user()
460 user2, token2 = generate_user()
461 user3, token3 = generate_user()
462 today_plus_2 = (today() + timedelta(days=2)).isoformat()
463 today_plus_3 = (today() + timedelta(days=3)).isoformat()
465 with requests_session(token1) as api:
466 request_id = api.CreateHostRequest(
467 requests_pb2.CreateHostRequestReq(
468 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
469 )
470 ).host_request_id
472 # another user can't access
473 with requests_session(token3) as api:
474 with pytest.raises(grpc.RpcError) as e:
475 api.RespondHostRequest(
476 requests_pb2.RespondHostRequestReq(
477 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
478 )
479 )
480 assert e.value.code() == grpc.StatusCode.NOT_FOUND
481 assert e.value.details() == "Couldn't find that host request."
483 with requests_session(token1) as api:
484 with pytest.raises(grpc.RpcError) as e:
485 api.RespondHostRequest(
486 requests_pb2.RespondHostRequestReq(
487 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
488 )
489 )
490 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
491 assert e.value.details() == "You are not the host of this request."
493 with requests_session(token2) as api:
494 # non existing id
495 with pytest.raises(grpc.RpcError) as e:
496 api.RespondHostRequest(
497 requests_pb2.RespondHostRequestReq(
498 host_request_id=9999, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
499 )
500 )
501 assert e.value.code() == grpc.StatusCode.NOT_FOUND
503 # host can't confirm or cancel (host should accept/reject)
504 with pytest.raises(grpc.RpcError) as e:
505 api.RespondHostRequest(
506 requests_pb2.RespondHostRequestReq(
507 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
508 )
509 )
510 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
511 assert e.value.details() == "You can't set the host request status to that."
512 with pytest.raises(grpc.RpcError) as e:
513 api.RespondHostRequest(
514 requests_pb2.RespondHostRequestReq(
515 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
516 )
517 )
518 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
519 assert e.value.details() == "You can't set the host request status to that."
521 api.RespondHostRequest(
522 requests_pb2.RespondHostRequestReq(
523 host_request_id=request_id,
524 status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED,
525 text="Test rejection message",
526 )
527 )
528 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=request_id))
529 assert res.messages[0].text.text == "Test rejection message"
530 assert res.messages[1].WhichOneof("content") == "host_request_status_changed"
531 assert res.messages[1].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
532 # should be able to move from rejected -> accepted
533 api.RespondHostRequest(
534 requests_pb2.RespondHostRequestReq(
535 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
536 )
537 )
539 with requests_session(token1) as api:
540 # can't make pending
541 with pytest.raises(grpc.RpcError) as e:
542 api.RespondHostRequest(
543 requests_pb2.RespondHostRequestReq(
544 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_PENDING
545 )
546 )
547 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
548 assert e.value.details() == "You can't set the host request status to that."
550 # can confirm then cancel
551 api.RespondHostRequest(
552 requests_pb2.RespondHostRequestReq(
553 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
554 )
555 )
557 api.RespondHostRequest(
558 requests_pb2.RespondHostRequestReq(
559 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
560 )
561 )
563 # can't confirm after having cancelled
564 with pytest.raises(grpc.RpcError) as e:
565 api.RespondHostRequest(
566 requests_pb2.RespondHostRequestReq(
567 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
568 )
569 )
570 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
571 assert e.value.details() == "You can't set the host request status to that."
573 # at this point there should be 7 messages
574 # 2 for creation, 2 for the status change with message, 3 for the other status changed
575 with requests_session(token1) as api:
576 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=request_id))
577 assert len(res.messages) == 7
578 assert res.messages[0].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
579 assert res.messages[1].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
580 assert res.messages[2].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
581 assert res.messages[4].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
582 assert res.messages[6].WhichOneof("content") == "chat_created"
585def test_get_host_request_messages(db):
586 user1, token1 = generate_user()
587 user2, token2 = generate_user()
588 today_plus_2 = (today() + timedelta(days=2)).isoformat()
589 today_plus_3 = (today() + timedelta(days=3)).isoformat()
590 with requests_session(token1) as api:
591 res = api.CreateHostRequest(
592 requests_pb2.CreateHostRequestReq(
593 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
594 )
595 )
596 conversation_id = res.host_request_id
598 add_message(db, "Test request 1 message 1", user1.id, conversation_id)
599 add_message(db, "Test request 1 message 2", user1.id, conversation_id)
600 add_message(db, "Test request 1 message 3", user1.id, conversation_id)
602 with requests_session(token2) as api:
603 api.RespondHostRequest(
604 requests_pb2.RespondHostRequestReq(
605 host_request_id=conversation_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
606 )
607 )
609 add_message(db, "Test request 1 message 4", user2.id, conversation_id)
610 add_message(db, "Test request 1 message 5", user2.id, conversation_id)
612 api.RespondHostRequest(
613 requests_pb2.RespondHostRequestReq(
614 host_request_id=conversation_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
615 )
616 )
618 with requests_session(token1) as api:
619 # 9 including initial message
620 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=conversation_id))
621 assert len(res.messages) == 9
622 assert res.no_more
624 res = api.GetHostRequestMessages(
625 requests_pb2.GetHostRequestMessagesReq(host_request_id=conversation_id, number=3)
626 )
627 assert not res.no_more
628 assert len(res.messages) == 3
629 assert res.messages[0].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
630 assert res.messages[0].WhichOneof("content") == "host_request_status_changed"
631 assert res.messages[1].text.text == "Test request 1 message 5"
632 assert res.messages[2].text.text == "Test request 1 message 4"
634 res = api.GetHostRequestMessages(
635 requests_pb2.GetHostRequestMessagesReq(
636 host_request_id=conversation_id,
637 last_message_id=res.messages[2].message_id,
638 number=6,
639 )
640 )
641 assert res.no_more
642 assert len(res.messages) == 6
643 assert res.messages[0].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
644 assert res.messages[0].WhichOneof("content") == "host_request_status_changed"
645 assert res.messages[1].text.text == "Test request 1 message 3"
646 assert res.messages[2].text.text == "Test request 1 message 2"
647 assert res.messages[3].text.text == "Test request 1 message 1"
648 assert res.messages[4].text.text == "Test request 1"
649 assert res.messages[5].WhichOneof("content") == "chat_created"
652def test_SendHostRequestMessage(db):
653 user1, token1 = generate_user()
654 user2, token2 = generate_user()
655 user3, token3 = generate_user()
656 today_plus_2 = (today() + timedelta(days=2)).isoformat()
657 today_plus_3 = (today() + timedelta(days=3)).isoformat()
658 with requests_session(token1) as api:
659 host_request_id = api.CreateHostRequest(
660 requests_pb2.CreateHostRequestReq(
661 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
662 )
663 ).host_request_id
665 with pytest.raises(grpc.RpcError) as e:
666 api.SendHostRequestMessage(
667 requests_pb2.SendHostRequestMessageReq(host_request_id=999, text="Test message 1")
668 )
669 assert e.value.code() == grpc.StatusCode.NOT_FOUND
671 with pytest.raises(grpc.RpcError) as e:
672 api.SendHostRequestMessage(requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text=""))
673 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
674 assert e.value.details() == "Invalid message."
676 api.SendHostRequestMessage(
677 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
678 )
679 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
680 assert res.messages[0].text.text == "Test message 1"
681 assert res.messages[0].author_user_id == user1.id
683 with requests_session(token3) as api:
684 # other user can't send
685 with pytest.raises(grpc.RpcError) as e:
686 api.SendHostRequestMessage(
687 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
688 )
689 assert e.value.code() == grpc.StatusCode.NOT_FOUND
690 assert e.value.details() == "Couldn't find that host request."
692 with requests_session(token2) as api:
693 api.SendHostRequestMessage(
694 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
695 )
696 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
697 # including 2 for creation control message and message
698 assert len(res.messages) == 4
699 assert res.messages[0].text.text == "Test message 2"
700 assert res.messages[0].author_user_id == user2.id
702 # CAN send messages to a rejected, confirmed or cancelled request, and for accepted
703 api.RespondHostRequest(
704 requests_pb2.RespondHostRequestReq(
705 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
706 )
707 )
708 api.SendHostRequestMessage(
709 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
710 )
712 api.RespondHostRequest(
713 requests_pb2.RespondHostRequestReq(
714 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
715 )
716 )
718 with requests_session(token1) as api:
719 api.RespondHostRequest(
720 requests_pb2.RespondHostRequestReq(
721 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
722 )
723 )
724 api.SendHostRequestMessage(
725 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
726 )
728 api.RespondHostRequest(
729 requests_pb2.RespondHostRequestReq(
730 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
731 )
732 )
733 api.SendHostRequestMessage(
734 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
735 )
738def test_get_updates(db):
739 user1, token1 = generate_user()
740 user2, token2 = generate_user()
741 user3, token3 = generate_user()
742 today_plus_2 = (today() + timedelta(days=2)).isoformat()
743 today_plus_3 = (today() + timedelta(days=3)).isoformat()
744 with requests_session(token1) as api:
745 host_request_id = api.CreateHostRequest(
746 requests_pb2.CreateHostRequestReq(
747 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
748 )
749 ).host_request_id
751 api.SendHostRequestMessage(
752 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
753 )
754 api.SendHostRequestMessage(
755 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
756 )
757 api.RespondHostRequest(
758 requests_pb2.RespondHostRequestReq(
759 host_request_id=host_request_id,
760 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
761 text="Test message 3",
762 )
763 )
765 api.CreateHostRequest(
766 requests_pb2.CreateHostRequestReq(
767 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 4"
768 )
769 )
771 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
772 assert len(res.messages) == 6
773 assert res.messages[0].text.text == "Test message 3"
774 assert res.messages[1].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
775 assert res.messages[2].text.text == "Test message 2"
776 assert res.messages[3].text.text == "Test message 1"
777 assert res.messages[4].text.text == "Test message 0"
778 message_id_3 = res.messages[0].message_id
779 message_id_cancel = res.messages[1].message_id
780 message_id_2 = res.messages[2].message_id
781 message_id_1 = res.messages[3].message_id
782 message_id_0 = res.messages[4].message_id
784 with pytest.raises(grpc.RpcError) as e:
785 api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=0))
786 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
788 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1))
789 assert res.no_more
790 assert len(res.updates) == 5
791 assert res.updates[0].message.text.text == "Test message 2"
792 assert (
793 res.updates[1].message.host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
794 )
795 assert res.updates[1].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
796 assert res.updates[2].message.text.text == "Test message 3"
797 assert res.updates[3].message.WhichOneof("content") == "chat_created"
798 assert res.updates[3].status == conversations_pb2.HOST_REQUEST_STATUS_PENDING
799 assert res.updates[4].message.text.text == "Test message 4"
801 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1, number=1))
802 assert not res.no_more
803 assert len(res.updates) == 1
804 assert res.updates[0].message.text.text == "Test message 2"
805 assert res.updates[0].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
807 with requests_session(token3) as api:
808 # other user can't access
809 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1))
810 assert len(res.updates) == 0
813def test_archive_host_request(db):
814 user1, token1 = generate_user()
815 user2, token2 = generate_user()
817 today_plus_2 = (today() + timedelta(days=2)).isoformat()
818 today_plus_3 = (today() + timedelta(days=3)).isoformat()
820 with requests_session(token1) as api:
821 host_request_id = api.CreateHostRequest(
822 requests_pb2.CreateHostRequestReq(
823 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
824 )
825 ).host_request_id
827 api.SendHostRequestMessage(
828 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
829 )
830 api.SendHostRequestMessage(
831 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
832 )
833 # happy path archiving host request
834 with requests_session(token1) as api:
835 api.RespondHostRequest(
836 requests_pb2.RespondHostRequestReq(
837 host_request_id=host_request_id,
838 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
839 text="Test message 3",
840 )
841 )
842 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True))
843 assert len(res.host_requests) == 1
844 assert res.host_requests[0].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
845 api.SetHostRequestArchiveStatus(
846 requests_pb2.SetHostRequestArchiveStatusReq(host_request_id=host_request_id, is_archived=True)
847 )
848 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_archived=True))
849 assert len(res.host_requests) == 1
852def test_mark_last_seen(db):
853 user1, token1 = generate_user()
854 user2, token2 = generate_user()
855 user3, token3 = generate_user()
856 today_plus_2 = (today() + timedelta(days=2)).isoformat()
857 today_plus_3 = (today() + timedelta(days=3)).isoformat()
858 with requests_session(token1) as api:
859 host_request_id = api.CreateHostRequest(
860 requests_pb2.CreateHostRequestReq(
861 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
862 )
863 ).host_request_id
865 host_request_id_2 = api.CreateHostRequest(
866 requests_pb2.CreateHostRequestReq(
867 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0a"
868 )
869 ).host_request_id
871 api.SendHostRequestMessage(
872 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
873 )
874 api.SendHostRequestMessage(
875 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
876 )
877 api.RespondHostRequest(
878 requests_pb2.RespondHostRequestReq(
879 host_request_id=host_request_id,
880 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
881 text="Test message 3",
882 )
883 )
885 # test Ping unseen host request count, should be automarked after sending
886 with api_session(token1) as api:
887 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 0
888 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
890 with api_session(token2) as api:
891 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 2
892 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
894 with requests_session(token2) as api:
895 assert api.ListHostRequests(requests_pb2.ListHostRequestsReq()).host_requests[0].last_seen_message_id == 0
897 api.MarkLastSeenHostRequest(
898 requests_pb2.MarkLastSeenHostRequestReq(host_request_id=host_request_id, last_seen_message_id=3)
899 )
901 assert api.ListHostRequests(requests_pb2.ListHostRequestsReq()).host_requests[0].last_seen_message_id == 3
903 with pytest.raises(grpc.RpcError) as e:
904 api.MarkLastSeenHostRequest(
905 requests_pb2.MarkLastSeenHostRequestReq(host_request_id=host_request_id, last_seen_message_id=1)
906 )
907 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
908 assert e.value.details() == "You can't unsee messages."
910 # this will be used to test sent request notifications
911 host_request_id_3 = api.CreateHostRequest(
912 requests_pb2.CreateHostRequestReq(
913 host_user_id=user1.id, from_date=today_plus_2, to_date=today_plus_3, text="Another test request"
914 )
915 ).host_request_id
917 # this should make id_2 all read
918 api.SendHostRequestMessage(
919 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id_2, text="Test")
920 )
922 with api_session(token2) as api:
923 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 1
924 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
926 # make sure sent and received count for unseen notifications
927 with requests_session(token1) as api:
928 api.SendHostRequestMessage(
929 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id_3, text="Test message")
930 )
932 with api_session(token2) as api:
933 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 1
934 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 1
937def test_response_rate(db):
938 user1, token1 = generate_user()
939 user2, token2 = generate_user()
940 user3, token3 = generate_user(delete_user=True)
942 today_plus_2 = (today() + timedelta(days=2)).isoformat()
943 today_plus_3 = (today() + timedelta(days=3)).isoformat()
945 with session_scope() as session:
946 refresh_materialized_view(session, "user_response_rates")
948 with requests_session(token1) as api:
949 # deleted: not found
950 with pytest.raises(grpc.RpcError) as e:
951 api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user3.id))
952 assert e.value.code() == grpc.StatusCode.NOT_FOUND
953 assert e.value.details() == "Couldn't find that user."
955 # no requests: insufficient
956 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
957 assert res.HasField("insufficient_data")
959 # send a request and back date it by 36 hours
960 host_request_1 = api.CreateHostRequest(
961 requests_pb2.CreateHostRequestReq(
962 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
963 )
964 ).host_request_id
965 with session_scope() as session:
966 session.execute(
967 select(Message)
968 .where(Message.conversation_id == host_request_1)
969 .where(Message.message_type == MessageType.chat_created)
970 ).scalar_one().time = now() - timedelta(hours=36)
971 refresh_materialized_view(session, "user_response_rates")
973 # still insufficient
974 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
975 assert res.HasField("insufficient_data")
977 # send a request and back date it by 35 hours
978 host_request_2 = api.CreateHostRequest(
979 requests_pb2.CreateHostRequestReq(
980 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
981 )
982 ).host_request_id
983 with session_scope() as session:
984 session.execute(
985 select(Message)
986 .where(Message.conversation_id == host_request_2)
987 .where(Message.message_type == MessageType.chat_created)
988 ).scalar_one().time = now() - timedelta(hours=35)
989 refresh_materialized_view(session, "user_response_rates")
991 # still insufficient
992 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
993 assert res.HasField("insufficient_data")
995 # send a request and back date it by 34 hours
996 host_request_3 = api.CreateHostRequest(
997 requests_pb2.CreateHostRequestReq(
998 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
999 )
1000 ).host_request_id
1001 with session_scope() as session:
1002 session.execute(
1003 select(Message)
1004 .where(Message.conversation_id == host_request_3)
1005 .where(Message.message_type == MessageType.chat_created)
1006 ).scalar_one().time = now() - timedelta(hours=34)
1007 refresh_materialized_view(session, "user_response_rates")
1009 # now low
1010 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1011 assert res.HasField("low")
1013 with requests_session(token2) as api:
1014 # accept a host req
1015 api.RespondHostRequest(
1016 requests_pb2.RespondHostRequestReq(
1017 host_request_id=host_request_2,
1018 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1019 text="Accepting host request",
1020 )
1021 )
1023 with session_scope() as session:
1024 refresh_materialized_view(session, "user_response_rates")
1026 with requests_session(token1) as api:
1027 # now some w p33 = 35h
1028 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1029 assert res.HasField("some")
1030 assert res.some.response_time_p33.ToTimedelta() == timedelta(hours=35)
1032 with requests_session(token2) as api:
1033 # accept another host req
1034 api.RespondHostRequest(
1035 requests_pb2.RespondHostRequestReq(
1036 host_request_id=host_request_3,
1037 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1038 text="Accepting host request",
1039 )
1040 )
1042 with session_scope() as session:
1043 refresh_materialized_view(session, "user_response_rates")
1045 with requests_session(token1) as api:
1046 # now most w p33 = 34h, p66 = 35h
1047 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1048 assert res.HasField("most")
1049 assert res.most.response_time_p33.ToTimedelta() == timedelta(hours=34)
1050 assert res.most.response_time_p66.ToTimedelta() == timedelta(hours=35)
1052 with requests_session(token2) as api:
1053 # accept last host req
1054 api.RespondHostRequest(
1055 requests_pb2.RespondHostRequestReq(
1056 host_request_id=host_request_1,
1057 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1058 text="Accepting host request",
1059 )
1060 )
1062 with session_scope() as session:
1063 refresh_materialized_view(session, "user_response_rates")
1065 with requests_session(token1) as api:
1066 # now all w p33 = 34h, p66 = 35h
1067 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1068 assert res.HasField("almost_all")
1069 assert res.almost_all.response_time_p33.ToTimedelta() == timedelta(hours=34)
1070 assert res.almost_all.response_time_p66.ToTimedelta() == timedelta(hours=35)
1072 # send a request and back date it by 2 hours
1073 host_request_4 = api.CreateHostRequest(
1074 requests_pb2.CreateHostRequestReq(
1075 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
1076 )
1077 ).host_request_id
1078 with session_scope() as session:
1079 session.execute(
1080 select(Message)
1081 .where(Message.conversation_id == host_request_4)
1082 .where(Message.message_type == MessageType.chat_created)
1083 ).scalar_one().time = now() - timedelta(hours=2)
1084 refresh_materialized_view(session, "user_response_rates")
1086 # send a request and back date it by 4 hours
1087 host_request_5 = api.CreateHostRequest(
1088 requests_pb2.CreateHostRequestReq(
1089 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
1090 )
1091 ).host_request_id
1092 with session_scope() as session:
1093 session.execute(
1094 select(Message)
1095 .where(Message.conversation_id == host_request_5)
1096 .where(Message.message_type == MessageType.chat_created)
1097 ).scalar_one().time = now() - timedelta(hours=4)
1098 refresh_materialized_view(session, "user_response_rates")
1100 # now some w p33 = 35h
1101 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1102 assert res.HasField("some")
1103 assert res.some.response_time_p33.ToTimedelta() == timedelta(hours=35)
1105 with requests_session(token2) as api:
1106 # accept host req
1107 api.RespondHostRequest(
1108 requests_pb2.RespondHostRequestReq(
1109 host_request_id=host_request_5,
1110 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1111 text="Accepting host request",
1112 )
1113 )
1115 with session_scope() as session:
1116 refresh_materialized_view(session, "user_response_rates")
1118 with requests_session(token1) as api:
1119 # now most w p33 = 34h, p66 = 36h
1120 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1121 assert res.HasField("most")
1122 assert res.most.response_time_p33.ToTimedelta() == timedelta(hours=34)
1123 assert res.most.response_time_p66.ToTimedelta() == timedelta(hours=36)
1125 with requests_session(token2) as api:
1126 # accept host req
1127 api.RespondHostRequest(
1128 requests_pb2.RespondHostRequestReq(
1129 host_request_id=host_request_4,
1130 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1131 text="Accepting host request",
1132 )
1133 )
1135 with session_scope() as session:
1136 refresh_materialized_view(session, "user_response_rates")
1138 with requests_session(token1) as api:
1139 # now most w p33 = 4h, p66 = 35h
1140 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1141 assert res.HasField("almost_all")
1142 assert res.almost_all.response_time_p33.ToTimedelta() == timedelta(hours=4)
1143 assert res.almost_all.response_time_p66.ToTimedelta() == timedelta(hours=35)
1146def test_request_notifications(db, push_collector):
1147 host, host_token = generate_user(complete_profile=True)
1148 surfer, surfer_token = generate_user(complete_profile=True)
1150 today_plus_2 = (today() + timedelta(days=2)).isoformat()
1151 today_plus_3 = (today() + timedelta(days=3)).isoformat()
1153 with requests_session(surfer_token) as api:
1154 with mock_notification_email() as mock:
1155 hr_id = api.CreateHostRequest(
1156 requests_pb2.CreateHostRequestReq(
1157 host_user_id=host.id,
1158 from_date=today_plus_2,
1159 to_date=today_plus_3,
1160 text="can i stay plz",
1161 )
1162 ).host_request_id
1164 mock.assert_called_once()
1165 e = email_fields(mock)
1166 assert e.recipient == host.email
1167 assert "host request" in e.subject.lower()
1168 assert host.name in e.plain
1169 assert host.name in e.html
1170 assert "quick decline" in e.plain.lower(), e.plain
1171 assert "quick decline" in e.html.lower()
1172 assert surfer.name in e.plain
1173 assert surfer.name in e.html
1174 assert v2date(today_plus_2, host) in e.plain
1175 assert v2date(today_plus_2, host) in e.html
1176 assert v2date(today_plus_3, host) in e.plain
1177 assert v2date(today_plus_3, host) in e.html
1178 assert "http://localhost:5001/img/thumbnail/" not in e.plain
1179 assert "http://localhost:5001/img/thumbnail/" in e.html
1180 assert f"http://localhost:3000/messages/request/{hr_id}" in e.plain
1181 assert f"http://localhost:3000/messages/request/{hr_id}" in e.html
1183 push_collector.assert_user_has_single_matching(
1184 host.id,
1185 title=f"{surfer.name} sent you a host request",
1186 )
1188 with requests_session(host_token) as api:
1189 with mock_notification_email() as mock:
1190 api.RespondHostRequest(
1191 requests_pb2.RespondHostRequestReq(
1192 host_request_id=hr_id,
1193 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1194 text="Accepting host request",
1195 )
1196 )
1198 e = email_fields(mock)
1199 assert e.recipient == surfer.email
1200 assert "host request" in e.subject.lower()
1201 assert host.name in e.plain
1202 assert host.name in e.html
1203 assert surfer.name in e.plain
1204 assert surfer.name in e.html
1205 assert v2date(today_plus_2, surfer) in e.plain
1206 assert v2date(today_plus_2, surfer) in e.html
1207 assert v2date(today_plus_3, surfer) in e.plain
1208 assert v2date(today_plus_3, surfer) in e.html
1209 assert "http://localhost:5001/img/thumbnail/" not in e.plain
1210 assert "http://localhost:5001/img/thumbnail/" in e.html
1211 assert f"http://localhost:3000/messages/request/{hr_id}" in e.plain
1212 assert f"http://localhost:3000/messages/request/{hr_id}" in e.html
1214 push_collector.assert_user_has_single_matching(
1215 surfer.id,
1216 title=f"{host.name} accepted your host request",
1217 )
1220def test_quick_decline(db, push_collector):
1221 host, host_token = generate_user(complete_profile=True)
1222 surfer, surfer_token = generate_user(complete_profile=True)
1224 today_plus_2 = (today() + timedelta(days=2)).isoformat()
1225 today_plus_3 = (today() + timedelta(days=3)).isoformat()
1227 with requests_session(surfer_token) as api:
1228 with mock_notification_email() as mock:
1229 hr_id = api.CreateHostRequest(
1230 requests_pb2.CreateHostRequestReq(
1231 host_user_id=host.id,
1232 from_date=today_plus_2,
1233 to_date=today_plus_3,
1234 text="can i stay plz",
1235 )
1236 ).host_request_id
1238 mock.assert_called_once()
1239 e = email_fields(mock)
1240 assert e.recipient == host.email
1241 assert "host request" in e.subject.lower()
1242 assert host.name in e.plain
1243 assert host.name in e.html
1244 assert "quick decline" in e.plain.lower(), e.plain
1245 assert "quick decline" in e.html.lower()
1246 assert surfer.name in e.plain
1247 assert surfer.name in e.html
1248 assert v2date(today_plus_2, host) in e.plain
1249 assert v2date(today_plus_2, host) in e.html
1250 assert v2date(today_plus_3, host) in e.plain
1251 assert v2date(today_plus_3, host) in e.html
1252 assert "http://localhost:5001/img/thumbnail/" not in e.plain
1253 assert "http://localhost:5001/img/thumbnail/" in e.html
1254 assert f"http://localhost:3000/messages/request/{hr_id}" in e.plain
1255 assert f"http://localhost:3000/messages/request/{hr_id}" in e.html
1257 push_collector.assert_user_has_single_matching(
1258 host.id,
1259 title=f"{surfer.name} sent you a host request",
1260 )
1262 # very ugly
1263 # http://localhost:3000/quick-link?payload=CAEiGAoOZnJpZW5kX3JlcXVlc3QSBmFjY2VwdA==&sig=BQdk024NTATm8zlR0krSXTBhP5U9TlFv7VhJeIHZtUg=
1264 for link in re.findall(r'<a href="(.*?)"', email_fields(mock).html):
1265 if "payload" not in link:
1266 continue
1267 print(link)
1268 url_parts = urlparse(link)
1269 params = parse_qs(url_parts.query)
1270 print(params["payload"][0])
1271 payload = unsubscribe_pb2.UnsubscribePayload.FromString(b64decode(params["payload"][0]))
1272 if payload.HasField("host_request_quick_decline"):
1273 with auth_api_session() as (auth_api, metadata_interceptor):
1274 res = auth_api.Unsubscribe(
1275 auth_pb2.UnsubscribeReq(
1276 payload=b64decode(params["payload"][0]),
1277 sig=b64decode(params["sig"][0]),
1278 )
1279 )
1280 assert res.response == "Thank you for responding to the host request!"
1281 break
1282 else:
1283 raise Exception("Didn't find link")
1285 with requests_session(surfer_token) as api:
1286 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=hr_id))
1287 assert res.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
1290def test_host_req_feedback(db):
1291 host, host_token = generate_user(complete_profile=True)
1292 host2, host2_token = generate_user(complete_profile=True)
1293 host3, host3_token = generate_user(complete_profile=True)
1294 surfer, surfer_token = generate_user(complete_profile=True)
1296 today_plus_2 = (today() + timedelta(days=2)).isoformat()
1297 today_plus_3 = (today() + timedelta(days=3)).isoformat()
1299 with requests_session(surfer_token) as api:
1300 hr_id = api.CreateHostRequest(
1301 requests_pb2.CreateHostRequestReq(
1302 host_user_id=host.id,
1303 from_date=today_plus_2,
1304 to_date=today_plus_3,
1305 text="can i stay plz",
1306 )
1307 ).host_request_id
1308 hr2_id = api.CreateHostRequest(
1309 requests_pb2.CreateHostRequestReq(
1310 host_user_id=host2.id,
1311 from_date=today_plus_2,
1312 to_date=today_plus_3,
1313 text="can i stay plz",
1314 )
1315 ).host_request_id
1316 hr3_id = api.CreateHostRequest(
1317 requests_pb2.CreateHostRequestReq(
1318 host_user_id=host3.id,
1319 from_date=today_plus_2,
1320 to_date=today_plus_3,
1321 text="can i stay plz",
1322 )
1323 ).host_request_id
1325 with requests_session(host_token) as api:
1326 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=hr_id))
1327 assert not res.need_host_request_feedback
1329 api.RespondHostRequest(
1330 requests_pb2.RespondHostRequestReq(
1331 host_request_id=hr_id,
1332 status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED,
1333 )
1334 )
1336 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=hr_id))
1337 assert res.need_host_request_feedback
1339 # surfer can't leave feedback
1340 with requests_session(surfer_token) as api:
1341 with pytest.raises(grpc.RpcError) as e:
1342 api.SendHostRequestFeedback(
1343 requests_pb2.SendHostRequestFeedbackReq(
1344 host_request_id=hr_id,
1345 )
1346 )
1347 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1348 assert e.value.details() == "Couldn't find that host request."
1350 with requests_session(host_token) as api:
1351 api.SendHostRequestFeedback(
1352 requests_pb2.SendHostRequestFeedbackReq(
1353 host_request_id=hr_id,
1354 host_request_quality=requests_pb2.HOST_REQUEST_QUALITY_LOW,
1355 )
1356 )
1357 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=hr_id))
1358 assert not res.need_host_request_feedback
1360 # can't leave it twice
1361 with requests_session(host_token) as api:
1362 with pytest.raises(grpc.RpcError) as e:
1363 api.SendHostRequestFeedback(
1364 requests_pb2.SendHostRequestFeedbackReq(
1365 host_request_id=hr_id,
1366 )
1367 )
1368 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1369 assert e.value.details() == "You have already left feedback for this host request!"
1371 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=hr_id))
1372 assert not res.need_host_request_feedback
1374 with requests_session(host2_token) as api:
1375 api.RespondHostRequest(
1376 requests_pb2.RespondHostRequestReq(
1377 host_request_id=hr2_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
1378 )
1379 )
1380 # can't leave feedback on the wrong one
1381 with pytest.raises(grpc.RpcError) as e:
1382 api.SendHostRequestFeedback(
1383 requests_pb2.SendHostRequestFeedbackReq(
1384 host_request_id=hr_id,
1385 )
1386 )
1387 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1388 assert e.value.details() == "Couldn't find that host request."
1390 # null feedback is still feedback
1391 api.SendHostRequestFeedback(requests_pb2.SendHostRequestFeedbackReq(host_request_id=hr2_id))
1393 with requests_session(host3_token) as api:
1394 api.RespondHostRequest(
1395 requests_pb2.RespondHostRequestReq(
1396 host_request_id=hr3_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
1397 )
1398 )
1400 api.SendHostRequestFeedback(
1401 requests_pb2.SendHostRequestFeedbackReq(host_request_id=hr3_id, decline_reason="bad req")
1402 )