Coverage for src/tests/test_requests.py: 99%
676 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-08-28 14:55 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-08-28 14:55 +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 import errors
10from couchers.crypto import b64decode
11from couchers.db import session_scope
12from couchers.materialized_views import refresh_materialized_view
13from couchers.models import (
14 Message,
15 MessageType,
16 RateLimitAction,
17)
18from couchers.rate_limits.definitions import RATE_LIMIT_DEFINITIONS, RATE_LIMIT_INTERVAL_STRING
19from couchers.sql import couchers_select as select
20from couchers.templates.v2 import v2date
21from couchers.utils import create_coordinate, now, today
22from proto import (
23 api_pb2,
24 auth_pb2,
25 conversations_pb2,
26 requests_pb2,
27)
28from proto.internal import unsubscribe_pb2
29from tests.test_fixtures import ( # noqa
30 api_session,
31 auth_api_session,
32 db,
33 email_fields,
34 generate_user,
35 mock_notification_email,
36 push_collector,
37 requests_session,
38 testconfig,
39)
42@pytest.fixture(autouse=True)
43def _(testconfig):
44 pass
47def test_create_request(db):
48 user1, token1 = generate_user()
49 hosting_city = "Morningside Heights, New York City"
50 hosting_lat = 40.8086
51 hosting_lng = -73.9616
52 hosting_radius = 500
53 user2, token2 = generate_user(
54 city=hosting_city,
55 geom=create_coordinate(hosting_lat, hosting_lng),
56 geom_radius=hosting_radius,
57 )
59 today_plus_2 = (today() + timedelta(days=2)).isoformat()
60 today_plus_3 = (today() + timedelta(days=3)).isoformat()
61 today_minus_2 = (today() - timedelta(days=2)).isoformat()
62 today_minus_3 = (today() - timedelta(days=3)).isoformat()
63 with requests_session(token1) as api:
64 with pytest.raises(grpc.RpcError) as e:
65 api.CreateHostRequest(
66 requests_pb2.CreateHostRequestReq(
67 host_user_id=user1.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
68 )
69 )
70 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
71 assert e.value.details() == errors.CANT_REQUEST_SELF
73 with pytest.raises(grpc.RpcError) as e:
74 api.CreateHostRequest(
75 requests_pb2.CreateHostRequestReq(
76 host_user_id=999, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
77 )
78 )
79 assert e.value.code() == grpc.StatusCode.NOT_FOUND
80 assert e.value.details() == errors.USER_NOT_FOUND
82 with pytest.raises(grpc.RpcError) as e:
83 api.CreateHostRequest(
84 requests_pb2.CreateHostRequestReq(
85 host_user_id=user2.id, from_date=today_plus_3, to_date=today_plus_2, text="Test request"
86 )
87 )
88 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
89 assert e.value.details() == errors.DATE_FROM_AFTER_TO
91 with pytest.raises(grpc.RpcError) as e:
92 api.CreateHostRequest(
93 requests_pb2.CreateHostRequestReq(
94 host_user_id=user2.id, from_date=today_minus_3, to_date=today_plus_2, text="Test request"
95 )
96 )
97 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
98 assert e.value.details() == errors.DATE_FROM_BEFORE_TODAY
100 with pytest.raises(grpc.RpcError) as e:
101 api.CreateHostRequest(
102 requests_pb2.CreateHostRequestReq(
103 host_user_id=user2.id, from_date=today_plus_2, to_date=today_minus_2, text="Test request"
104 )
105 )
106 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
107 assert e.value.details() == errors.DATE_FROM_AFTER_TO
109 with pytest.raises(grpc.RpcError) as e:
110 api.CreateHostRequest(
111 requests_pb2.CreateHostRequestReq(
112 host_user_id=user2.id, from_date="2020-00-06", to_date=today_minus_2, text="Test request"
113 )
114 )
115 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
116 assert e.value.details() == errors.INVALID_DATE
118 res = api.CreateHostRequest(
119 requests_pb2.CreateHostRequestReq(
120 host_user_id=user2.id,
121 from_date=today_plus_2,
122 to_date=today_plus_3,
123 text="Test request",
124 )
125 )
127 host_requests = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True)).host_requests
129 assert len(host_requests) == 1
130 hr = host_requests[0]
132 assert hr.latest_message.text.text == "Test request"
134 assert hr.hosting_city == hosting_city
135 assert round(hr.hosting_lat, 4) == hosting_lat
136 assert round(hr.hosting_lng, 4) == hosting_lng
137 assert hr.hosting_radius == hosting_radius
139 today_ = today()
140 today_plus_one_year = today_ + timedelta(days=365)
141 today_plus_one_year_plus_2 = (today_plus_one_year + timedelta(days=2)).isoformat()
142 today_plus_one_year_plus_3 = (today_plus_one_year + timedelta(days=3)).isoformat()
143 with pytest.raises(grpc.RpcError) as e:
144 api.CreateHostRequest(
145 requests_pb2.CreateHostRequestReq(
146 host_user_id=user2.id,
147 from_date=today_plus_one_year_plus_2,
148 to_date=today_plus_one_year_plus_3,
149 text="Test from date after one year",
150 )
151 )
152 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
153 assert e.value.details() == errors.DATE_FROM_AFTER_ONE_YEAR
155 with pytest.raises(grpc.RpcError) as e:
156 api.CreateHostRequest(
157 requests_pb2.CreateHostRequestReq(
158 host_user_id=user2.id,
159 from_date=today_plus_2,
160 to_date=today_plus_one_year_plus_3,
161 text="Test to date one year after from date",
162 )
163 )
164 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
165 assert e.value.details() == errors.DATE_TO_AFTER_ONE_YEAR
168def test_create_request_incomplete_profile(db):
169 user1, token1 = generate_user(complete_profile=False)
170 user2, _ = generate_user()
171 today_plus_2 = (today() + timedelta(days=2)).isoformat()
172 today_plus_3 = (today() + timedelta(days=3)).isoformat()
173 with requests_session(token1) as api:
174 with pytest.raises(grpc.RpcError) as e:
175 api.CreateHostRequest(
176 requests_pb2.CreateHostRequestReq(
177 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
178 )
179 )
180 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
181 assert e.value.details() == errors.INCOMPLETE_PROFILE_SEND_REQUEST
184def test_excessive_requests_are_reported(db):
185 """Test that excessive host requests are first reported in a warning email and finally lead blocking of further requests."""
186 user, token = generate_user()
187 today_plus_2 = (today() + timedelta(days=2)).isoformat()
188 today_plus_3 = (today() + timedelta(days=3)).isoformat()
189 rate_limit_definition = RATE_LIMIT_DEFINITIONS[RateLimitAction.host_request]
190 with requests_session(token) as api:
191 # Test warning email
192 with mock_notification_email() as mock_email:
193 for _ in range(rate_limit_definition.warning_limit):
194 host_user, _ = generate_user()
195 _ = api.CreateHostRequest(
196 requests_pb2.CreateHostRequestReq(
197 host_user_id=host_user.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
198 )
199 )
201 assert mock_email.call_count == 0
202 host_user, _ = generate_user()
203 _ = api.CreateHostRequest(
204 requests_pb2.CreateHostRequestReq(
205 host_user_id=host_user.id,
206 from_date=today_plus_2,
207 to_date=today_plus_3,
208 text="Excessive test request",
209 )
210 )
211 assert mock_email.call_count == 1
212 email = mock_email.mock_calls[0].kwargs["plain"]
213 assert email.startswith(
214 f"User {user.username} has sent {rate_limit_definition.warning_limit} host requests in the past {RATE_LIMIT_INTERVAL_STRING}."
215 )
217 # Test ban after exceeding HOST_REQUEST_HARD_LIMIT
218 with mock_notification_email() as mock_email:
219 for _ in range(rate_limit_definition.hard_limit - rate_limit_definition.warning_limit - 1):
220 host_user, _ = generate_user()
221 _ = api.CreateHostRequest(
222 requests_pb2.CreateHostRequestReq(
223 host_user_id=host_user.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
224 )
225 )
227 assert mock_email.call_count == 0
228 host_user, _ = generate_user()
229 with pytest.raises(grpc.RpcError) as exc_info:
230 _ = api.CreateHostRequest(
231 requests_pb2.CreateHostRequestReq(
232 host_user_id=host_user.id,
233 from_date=today_plus_2,
234 to_date=today_plus_3,
235 text="Excessive test request",
236 )
237 )
238 assert exc_info.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED
239 assert exc_info.value.details() == errors.HOST_REQUEST_RATE_LIMIT
241 assert mock_email.call_count == 1
242 email = mock_email.mock_calls[0].kwargs["plain"]
243 assert email.startswith(
244 f"User {user.username} has sent {rate_limit_definition.hard_limit} host requests in the past {RATE_LIMIT_INTERVAL_STRING}."
245 )
246 assert "The user has been blocked from sending further host requests for now." in email
249def add_message(db, text, author_id, conversation_id):
250 with session_scope() as session:
251 message = Message(
252 conversation_id=conversation_id, author_id=author_id, text=text, message_type=MessageType.text
253 )
255 session.add(message)
258def test_GetHostRequest(db):
259 user1, token1 = generate_user()
260 user2, token2 = generate_user()
261 user3, token3 = generate_user()
262 today_plus_2 = (today() + timedelta(days=2)).isoformat()
263 today_plus_3 = (today() + timedelta(days=3)).isoformat()
264 with requests_session(token1) as api:
265 host_request_id = api.CreateHostRequest(
266 requests_pb2.CreateHostRequestReq(
267 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
268 )
269 ).host_request_id
271 with pytest.raises(grpc.RpcError) as e:
272 api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=999))
273 assert e.value.code() == grpc.StatusCode.NOT_FOUND
274 assert e.value.details() == errors.HOST_REQUEST_NOT_FOUND
276 api.SendHostRequestMessage(
277 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
278 )
280 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=host_request_id))
281 assert res.latest_message.text.text == "Test message 1"
284def test_ListHostRequests(db):
285 user1, token1 = generate_user()
286 user2, token2 = generate_user()
287 user3, token3 = generate_user()
288 today_plus_2 = (today() + timedelta(days=2)).isoformat()
289 today_plus_3 = (today() + timedelta(days=3)).isoformat()
290 with requests_session(token1) as api:
291 host_request_1 = api.CreateHostRequest(
292 requests_pb2.CreateHostRequestReq(
293 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
294 )
295 ).host_request_id
297 host_request_2 = api.CreateHostRequest(
298 requests_pb2.CreateHostRequestReq(
299 host_user_id=user3.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 2"
300 )
301 ).host_request_id
303 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True))
304 assert res.no_more
305 assert len(res.host_requests) == 2
307 with requests_session(token2) as api:
308 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
309 assert res.no_more
310 assert len(res.host_requests) == 1
311 assert res.host_requests[0].latest_message.text.text == "Test request 1"
312 assert res.host_requests[0].surfer_user_id == user1.id
313 assert res.host_requests[0].host_user_id == user2.id
314 assert res.host_requests[0].status == conversations_pb2.HOST_REQUEST_STATUS_PENDING
316 add_message(db, "Test request 1 message 1", user2.id, host_request_1)
317 add_message(db, "Test request 1 message 2", user2.id, host_request_1)
318 add_message(db, "Test request 1 message 3", user2.id, host_request_1)
320 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
321 assert res.host_requests[0].latest_message.text.text == "Test request 1 message 3"
323 api.CreateHostRequest(
324 requests_pb2.CreateHostRequestReq(
325 host_user_id=user1.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 3"
326 )
327 )
329 add_message(db, "Test request 2 message 1", user1.id, host_request_2)
330 add_message(db, "Test request 2 message 2", user3.id, host_request_2)
332 with requests_session(token3) as api:
333 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
334 assert res.no_more
335 assert len(res.host_requests) == 1
336 assert res.host_requests[0].latest_message.text.text == "Test request 2 message 2"
338 with requests_session(token1) as api:
339 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
340 assert len(res.host_requests) == 1
342 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq())
343 assert len(res.host_requests) == 3
346def test_ListHostRequests_pagination_regression(db):
347 """
348 ListHostRequests was skipping a request when getting multiple pages
349 """
350 user1, token1 = generate_user()
351 user2, token2 = generate_user()
352 today_plus_2 = (today() + timedelta(days=2)).isoformat()
353 today_plus_3 = (today() + timedelta(days=3)).isoformat()
354 with requests_session(token1) as api:
355 host_request_1 = api.CreateHostRequest(
356 requests_pb2.CreateHostRequestReq(
357 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
358 )
359 ).host_request_id
361 host_request_2 = api.CreateHostRequest(
362 requests_pb2.CreateHostRequestReq(
363 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 2"
364 )
365 ).host_request_id
367 host_request_3 = api.CreateHostRequest(
368 requests_pb2.CreateHostRequestReq(
369 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 3"
370 )
371 ).host_request_id
373 with requests_session(token2) as api:
374 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
375 assert res.no_more
376 assert len(res.host_requests) == 3
377 assert res.host_requests[0].latest_message.text.text == "Test request 3"
378 assert res.host_requests[1].latest_message.text.text == "Test request 2"
379 assert res.host_requests[2].latest_message.text.text == "Test request 1"
381 with requests_session(token2) as api:
382 api.RespondHostRequest(
383 requests_pb2.RespondHostRequestReq(
384 host_request_id=host_request_2,
385 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
386 text="Accepting host request 2",
387 )
388 )
389 api.RespondHostRequest(
390 requests_pb2.RespondHostRequestReq(
391 host_request_id=host_request_1,
392 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
393 text="Accepting host request 1",
394 )
395 )
396 api.RespondHostRequest(
397 requests_pb2.RespondHostRequestReq(
398 host_request_id=host_request_3,
399 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
400 text="Accepting host request 3",
401 )
402 )
404 with requests_session(token2) as api:
405 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
406 assert res.no_more
407 assert len(res.host_requests) == 3
408 assert res.host_requests[0].latest_message.text.text == "Accepting host request 3"
409 assert res.host_requests[1].latest_message.text.text == "Accepting host request 1"
410 assert res.host_requests[2].latest_message.text.text == "Accepting host request 2"
412 with requests_session(token2) as api:
413 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True, number=1))
414 assert not res.no_more
415 assert len(res.host_requests) == 1
416 assert res.host_requests[0].latest_message.text.text == "Accepting host request 3"
417 res = api.ListHostRequests(
418 requests_pb2.ListHostRequestsReq(only_received=True, number=1, last_request_id=res.last_request_id)
419 )
420 assert not res.no_more
421 assert len(res.host_requests) == 1
422 assert res.host_requests[0].latest_message.text.text == "Accepting host request 1"
423 res = api.ListHostRequests(
424 requests_pb2.ListHostRequestsReq(only_received=True, number=1, last_request_id=res.last_request_id)
425 )
426 assert res.no_more
427 assert len(res.host_requests) == 1
428 assert res.host_requests[0].latest_message.text.text == "Accepting host request 2"
431def test_ListHostRequests_active_filter(db):
432 user1, token1 = generate_user()
433 user2, token2 = generate_user()
434 today_plus_2 = (today() + timedelta(days=2)).isoformat()
435 today_plus_3 = (today() + timedelta(days=3)).isoformat()
437 with requests_session(token1) as api:
438 request_id = api.CreateHostRequest(
439 requests_pb2.CreateHostRequestReq(
440 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
441 )
442 ).host_request_id
443 api.RespondHostRequest(
444 requests_pb2.RespondHostRequestReq(
445 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
446 )
447 )
449 with requests_session(token2) as api:
450 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
451 assert len(res.host_requests) == 1
452 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_active=True))
453 assert len(res.host_requests) == 0
456def test_RespondHostRequests(db):
457 user1, token1 = generate_user()
458 user2, token2 = generate_user()
459 user3, token3 = generate_user()
460 today_plus_2 = (today() + timedelta(days=2)).isoformat()
461 today_plus_3 = (today() + timedelta(days=3)).isoformat()
463 with requests_session(token1) as api:
464 request_id = api.CreateHostRequest(
465 requests_pb2.CreateHostRequestReq(
466 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
467 )
468 ).host_request_id
470 # another user can't access
471 with requests_session(token3) as api:
472 with pytest.raises(grpc.RpcError) as e:
473 api.RespondHostRequest(
474 requests_pb2.RespondHostRequestReq(
475 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
476 )
477 )
478 assert e.value.code() == grpc.StatusCode.NOT_FOUND
479 assert e.value.details() == errors.HOST_REQUEST_NOT_FOUND
481 with requests_session(token1) as api:
482 with pytest.raises(grpc.RpcError) as e:
483 api.RespondHostRequest(
484 requests_pb2.RespondHostRequestReq(
485 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
486 )
487 )
488 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
489 assert e.value.details() == errors.NOT_THE_HOST
491 with requests_session(token2) as api:
492 # non existing id
493 with pytest.raises(grpc.RpcError) as e:
494 api.RespondHostRequest(
495 requests_pb2.RespondHostRequestReq(
496 host_request_id=9999, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
497 )
498 )
499 assert e.value.code() == grpc.StatusCode.NOT_FOUND
501 # host can't confirm or cancel (host should accept/reject)
502 with pytest.raises(grpc.RpcError) as e:
503 api.RespondHostRequest(
504 requests_pb2.RespondHostRequestReq(
505 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
506 )
507 )
508 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
509 assert e.value.details() == errors.INVALID_HOST_REQUEST_STATUS
510 with pytest.raises(grpc.RpcError) as e:
511 api.RespondHostRequest(
512 requests_pb2.RespondHostRequestReq(
513 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
514 )
515 )
516 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
517 assert e.value.details() == errors.INVALID_HOST_REQUEST_STATUS
519 api.RespondHostRequest(
520 requests_pb2.RespondHostRequestReq(
521 host_request_id=request_id,
522 status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED,
523 text="Test rejection message",
524 )
525 )
526 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=request_id))
527 assert res.messages[0].text.text == "Test rejection message"
528 assert res.messages[1].WhichOneof("content") == "host_request_status_changed"
529 assert res.messages[1].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
530 # should be able to move from rejected -> accepted
531 api.RespondHostRequest(
532 requests_pb2.RespondHostRequestReq(
533 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
534 )
535 )
537 with requests_session(token1) as api:
538 # can't make pending
539 with pytest.raises(grpc.RpcError) as e:
540 api.RespondHostRequest(
541 requests_pb2.RespondHostRequestReq(
542 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_PENDING
543 )
544 )
545 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
546 assert e.value.details() == errors.INVALID_HOST_REQUEST_STATUS
548 # can confirm then cancel
549 api.RespondHostRequest(
550 requests_pb2.RespondHostRequestReq(
551 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
552 )
553 )
555 api.RespondHostRequest(
556 requests_pb2.RespondHostRequestReq(
557 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
558 )
559 )
561 # can't confirm after having cancelled
562 with pytest.raises(grpc.RpcError) as e:
563 api.RespondHostRequest(
564 requests_pb2.RespondHostRequestReq(
565 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
566 )
567 )
568 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
569 assert e.value.details() == errors.INVALID_HOST_REQUEST_STATUS
571 # at this point there should be 7 messages
572 # 2 for creation, 2 for the status change with message, 3 for the other status changed
573 with requests_session(token1) as api:
574 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=request_id))
575 assert len(res.messages) == 7
576 assert res.messages[0].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
577 assert res.messages[1].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
578 assert res.messages[2].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
579 assert res.messages[4].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
580 assert res.messages[6].WhichOneof("content") == "chat_created"
583def test_get_host_request_messages(db):
584 user1, token1 = generate_user()
585 user2, token2 = generate_user()
586 today_plus_2 = (today() + timedelta(days=2)).isoformat()
587 today_plus_3 = (today() + timedelta(days=3)).isoformat()
588 with requests_session(token1) as api:
589 res = api.CreateHostRequest(
590 requests_pb2.CreateHostRequestReq(
591 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
592 )
593 )
594 conversation_id = res.host_request_id
596 add_message(db, "Test request 1 message 1", user1.id, conversation_id)
597 add_message(db, "Test request 1 message 2", user1.id, conversation_id)
598 add_message(db, "Test request 1 message 3", user1.id, conversation_id)
600 with requests_session(token2) as api:
601 api.RespondHostRequest(
602 requests_pb2.RespondHostRequestReq(
603 host_request_id=conversation_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
604 )
605 )
607 add_message(db, "Test request 1 message 4", user2.id, conversation_id)
608 add_message(db, "Test request 1 message 5", user2.id, conversation_id)
610 api.RespondHostRequest(
611 requests_pb2.RespondHostRequestReq(
612 host_request_id=conversation_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
613 )
614 )
616 with requests_session(token1) as api:
617 # 9 including initial message
618 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=conversation_id))
619 assert len(res.messages) == 9
620 assert res.no_more
622 res = api.GetHostRequestMessages(
623 requests_pb2.GetHostRequestMessagesReq(host_request_id=conversation_id, number=3)
624 )
625 assert not res.no_more
626 assert len(res.messages) == 3
627 assert res.messages[0].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
628 assert res.messages[0].WhichOneof("content") == "host_request_status_changed"
629 assert res.messages[1].text.text == "Test request 1 message 5"
630 assert res.messages[2].text.text == "Test request 1 message 4"
632 res = api.GetHostRequestMessages(
633 requests_pb2.GetHostRequestMessagesReq(
634 host_request_id=conversation_id,
635 last_message_id=res.messages[2].message_id,
636 number=6,
637 )
638 )
639 assert res.no_more
640 assert len(res.messages) == 6
641 assert res.messages[0].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
642 assert res.messages[0].WhichOneof("content") == "host_request_status_changed"
643 assert res.messages[1].text.text == "Test request 1 message 3"
644 assert res.messages[2].text.text == "Test request 1 message 2"
645 assert res.messages[3].text.text == "Test request 1 message 1"
646 assert res.messages[4].text.text == "Test request 1"
647 assert res.messages[5].WhichOneof("content") == "chat_created"
650def test_SendHostRequestMessage(db):
651 user1, token1 = generate_user()
652 user2, token2 = generate_user()
653 user3, token3 = generate_user()
654 today_plus_2 = (today() + timedelta(days=2)).isoformat()
655 today_plus_3 = (today() + timedelta(days=3)).isoformat()
656 with requests_session(token1) as api:
657 host_request_id = api.CreateHostRequest(
658 requests_pb2.CreateHostRequestReq(
659 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
660 )
661 ).host_request_id
663 with pytest.raises(grpc.RpcError) as e:
664 api.SendHostRequestMessage(
665 requests_pb2.SendHostRequestMessageReq(host_request_id=999, text="Test message 1")
666 )
667 assert e.value.code() == grpc.StatusCode.NOT_FOUND
669 with pytest.raises(grpc.RpcError) as e:
670 api.SendHostRequestMessage(requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text=""))
671 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
672 assert e.value.details() == errors.INVALID_MESSAGE
674 api.SendHostRequestMessage(
675 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
676 )
677 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
678 assert res.messages[0].text.text == "Test message 1"
679 assert res.messages[0].author_user_id == user1.id
681 with requests_session(token3) as api:
682 # other user can't send
683 with pytest.raises(grpc.RpcError) as e:
684 api.SendHostRequestMessage(
685 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
686 )
687 assert e.value.code() == grpc.StatusCode.NOT_FOUND
688 assert e.value.details() == errors.HOST_REQUEST_NOT_FOUND
690 with requests_session(token2) as api:
691 api.SendHostRequestMessage(
692 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
693 )
694 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
695 # including 2 for creation control message and message
696 assert len(res.messages) == 4
697 assert res.messages[0].text.text == "Test message 2"
698 assert res.messages[0].author_user_id == user2.id
700 # CAN send messages to a rejected, confirmed or cancelled request, and for accepted
701 api.RespondHostRequest(
702 requests_pb2.RespondHostRequestReq(
703 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
704 )
705 )
706 api.SendHostRequestMessage(
707 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
708 )
710 api.RespondHostRequest(
711 requests_pb2.RespondHostRequestReq(
712 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
713 )
714 )
716 with requests_session(token1) as api:
717 api.RespondHostRequest(
718 requests_pb2.RespondHostRequestReq(
719 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
720 )
721 )
722 api.SendHostRequestMessage(
723 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
724 )
726 api.RespondHostRequest(
727 requests_pb2.RespondHostRequestReq(
728 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
729 )
730 )
731 api.SendHostRequestMessage(
732 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
733 )
736def test_get_updates(db):
737 user1, token1 = generate_user()
738 user2, token2 = generate_user()
739 user3, token3 = generate_user()
740 today_plus_2 = (today() + timedelta(days=2)).isoformat()
741 today_plus_3 = (today() + timedelta(days=3)).isoformat()
742 with requests_session(token1) as api:
743 host_request_id = api.CreateHostRequest(
744 requests_pb2.CreateHostRequestReq(
745 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
746 )
747 ).host_request_id
749 api.SendHostRequestMessage(
750 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
751 )
752 api.SendHostRequestMessage(
753 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
754 )
755 api.RespondHostRequest(
756 requests_pb2.RespondHostRequestReq(
757 host_request_id=host_request_id,
758 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
759 text="Test message 3",
760 )
761 )
763 api.CreateHostRequest(
764 requests_pb2.CreateHostRequestReq(
765 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 4"
766 )
767 )
769 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
770 assert len(res.messages) == 6
771 assert res.messages[0].text.text == "Test message 3"
772 assert res.messages[1].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
773 assert res.messages[2].text.text == "Test message 2"
774 assert res.messages[3].text.text == "Test message 1"
775 assert res.messages[4].text.text == "Test message 0"
776 message_id_3 = res.messages[0].message_id
777 message_id_cancel = res.messages[1].message_id
778 message_id_2 = res.messages[2].message_id
779 message_id_1 = res.messages[3].message_id
780 message_id_0 = res.messages[4].message_id
782 with pytest.raises(grpc.RpcError) as e:
783 api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=0))
784 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
786 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1))
787 assert res.no_more
788 assert len(res.updates) == 5
789 assert res.updates[0].message.text.text == "Test message 2"
790 assert (
791 res.updates[1].message.host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
792 )
793 assert res.updates[1].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
794 assert res.updates[2].message.text.text == "Test message 3"
795 assert res.updates[3].message.WhichOneof("content") == "chat_created"
796 assert res.updates[3].status == conversations_pb2.HOST_REQUEST_STATUS_PENDING
797 assert res.updates[4].message.text.text == "Test message 4"
799 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1, number=1))
800 assert not res.no_more
801 assert len(res.updates) == 1
802 assert res.updates[0].message.text.text == "Test message 2"
803 assert res.updates[0].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
805 with requests_session(token3) as api:
806 # other user can't access
807 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1))
808 assert len(res.updates) == 0
811def test_archive_host_request(db):
812 user1, token1 = generate_user()
813 user2, token2 = generate_user()
815 today_plus_2 = (today() + timedelta(days=2)).isoformat()
816 today_plus_3 = (today() + timedelta(days=3)).isoformat()
818 with requests_session(token1) as api:
819 host_request_id = api.CreateHostRequest(
820 requests_pb2.CreateHostRequestReq(
821 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
822 )
823 ).host_request_id
825 api.SendHostRequestMessage(
826 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
827 )
828 api.SendHostRequestMessage(
829 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
830 )
831 # happy path archiving host request
832 with requests_session(token1) as api:
833 api.RespondHostRequest(
834 requests_pb2.RespondHostRequestReq(
835 host_request_id=host_request_id,
836 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
837 text="Test message 3",
838 )
839 )
840 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True))
841 assert len(res.host_requests) == 1
842 assert res.host_requests[0].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
843 api.SetHostRequestArchiveStatus(
844 requests_pb2.SetHostRequestArchiveStatusReq(host_request_id=host_request_id, is_archived=True)
845 )
846 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_archived=True))
847 assert len(res.host_requests) == 1
850def test_mark_last_seen(db):
851 user1, token1 = generate_user()
852 user2, token2 = generate_user()
853 user3, token3 = generate_user()
854 today_plus_2 = (today() + timedelta(days=2)).isoformat()
855 today_plus_3 = (today() + timedelta(days=3)).isoformat()
856 with requests_session(token1) as api:
857 host_request_id = api.CreateHostRequest(
858 requests_pb2.CreateHostRequestReq(
859 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
860 )
861 ).host_request_id
863 host_request_id_2 = api.CreateHostRequest(
864 requests_pb2.CreateHostRequestReq(
865 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0a"
866 )
867 ).host_request_id
869 api.SendHostRequestMessage(
870 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
871 )
872 api.SendHostRequestMessage(
873 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
874 )
875 api.RespondHostRequest(
876 requests_pb2.RespondHostRequestReq(
877 host_request_id=host_request_id,
878 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
879 text="Test message 3",
880 )
881 )
883 # test Ping unseen host request count, should be automarked after sending
884 with api_session(token1) as api:
885 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 0
886 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
888 with api_session(token2) as api:
889 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 2
890 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
892 with requests_session(token2) as api:
893 assert api.ListHostRequests(requests_pb2.ListHostRequestsReq()).host_requests[0].last_seen_message_id == 0
895 api.MarkLastSeenHostRequest(
896 requests_pb2.MarkLastSeenHostRequestReq(host_request_id=host_request_id, last_seen_message_id=3)
897 )
899 assert api.ListHostRequests(requests_pb2.ListHostRequestsReq()).host_requests[0].last_seen_message_id == 3
901 with pytest.raises(grpc.RpcError) as e:
902 api.MarkLastSeenHostRequest(
903 requests_pb2.MarkLastSeenHostRequestReq(host_request_id=host_request_id, last_seen_message_id=1)
904 )
905 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
906 assert e.value.details() == errors.CANT_UNSEE_MESSAGES
908 # this will be used to test sent request notifications
909 host_request_id_3 = api.CreateHostRequest(
910 requests_pb2.CreateHostRequestReq(
911 host_user_id=user1.id, from_date=today_plus_2, to_date=today_plus_3, text="Another test request"
912 )
913 ).host_request_id
915 # this should make id_2 all read
916 api.SendHostRequestMessage(
917 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id_2, text="Test")
918 )
920 with api_session(token2) as api:
921 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 1
922 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
924 # make sure sent and received count for unseen notifications
925 with requests_session(token1) as api:
926 api.SendHostRequestMessage(
927 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id_3, text="Test message")
928 )
930 with api_session(token2) as api:
931 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 1
932 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 1
935def test_response_rate(db):
936 user1, token1 = generate_user()
937 user2, token2 = generate_user()
938 user3, token3 = generate_user(delete_user=True)
940 today_plus_2 = (today() + timedelta(days=2)).isoformat()
941 today_plus_3 = (today() + timedelta(days=3)).isoformat()
943 with session_scope() as session:
944 refresh_materialized_view(session, "user_response_rates")
946 with requests_session(token1) as api:
947 # deleted: not found
948 with pytest.raises(grpc.RpcError) as e:
949 api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user3.id))
950 assert e.value.code() == grpc.StatusCode.NOT_FOUND
951 assert e.value.details() == errors.USER_NOT_FOUND
953 # no requests: insufficient
954 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
955 assert res.HasField("insufficient_data")
957 # send a request and back date it by 36 hours
958 host_request_1 = api.CreateHostRequest(
959 requests_pb2.CreateHostRequestReq(
960 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
961 )
962 ).host_request_id
963 with session_scope() as session:
964 session.execute(
965 select(Message)
966 .where(Message.conversation_id == host_request_1)
967 .where(Message.message_type == MessageType.chat_created)
968 ).scalar_one().time = now() - timedelta(hours=36)
969 refresh_materialized_view(session, "user_response_rates")
971 # still insufficient
972 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
973 assert res.HasField("insufficient_data")
975 # send a request and back date it by 35 hours
976 host_request_2 = api.CreateHostRequest(
977 requests_pb2.CreateHostRequestReq(
978 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
979 )
980 ).host_request_id
981 with session_scope() as session:
982 session.execute(
983 select(Message)
984 .where(Message.conversation_id == host_request_2)
985 .where(Message.message_type == MessageType.chat_created)
986 ).scalar_one().time = now() - timedelta(hours=35)
987 refresh_materialized_view(session, "user_response_rates")
989 # still insufficient
990 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
991 assert res.HasField("insufficient_data")
993 # send a request and back date it by 34 hours
994 host_request_3 = api.CreateHostRequest(
995 requests_pb2.CreateHostRequestReq(
996 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
997 )
998 ).host_request_id
999 with session_scope() as session:
1000 session.execute(
1001 select(Message)
1002 .where(Message.conversation_id == host_request_3)
1003 .where(Message.message_type == MessageType.chat_created)
1004 ).scalar_one().time = now() - timedelta(hours=34)
1005 refresh_materialized_view(session, "user_response_rates")
1007 # now low
1008 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1009 assert res.HasField("low")
1011 with requests_session(token2) as api:
1012 # accept a host req
1013 api.RespondHostRequest(
1014 requests_pb2.RespondHostRequestReq(
1015 host_request_id=host_request_2,
1016 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1017 text="Accepting host request",
1018 )
1019 )
1021 with session_scope() as session:
1022 refresh_materialized_view(session, "user_response_rates")
1024 with requests_session(token1) as api:
1025 # now some w p33 = 35h
1026 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1027 assert res.HasField("some")
1028 assert res.some.response_time_p33.ToTimedelta() == timedelta(hours=35)
1030 with requests_session(token2) as api:
1031 # accept another host req
1032 api.RespondHostRequest(
1033 requests_pb2.RespondHostRequestReq(
1034 host_request_id=host_request_3,
1035 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1036 text="Accepting host request",
1037 )
1038 )
1040 with session_scope() as session:
1041 refresh_materialized_view(session, "user_response_rates")
1043 with requests_session(token1) as api:
1044 # now most w p33 = 34h, p66 = 35h
1045 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1046 assert res.HasField("most")
1047 assert res.most.response_time_p33.ToTimedelta() == timedelta(hours=34)
1048 assert res.most.response_time_p66.ToTimedelta() == timedelta(hours=35)
1050 with requests_session(token2) as api:
1051 # accept last host req
1052 api.RespondHostRequest(
1053 requests_pb2.RespondHostRequestReq(
1054 host_request_id=host_request_1,
1055 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1056 text="Accepting host request",
1057 )
1058 )
1060 with session_scope() as session:
1061 refresh_materialized_view(session, "user_response_rates")
1063 with requests_session(token1) as api:
1064 # now all w p33 = 34h, p66 = 35h
1065 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1066 assert res.HasField("almost_all")
1067 assert res.almost_all.response_time_p33.ToTimedelta() == timedelta(hours=34)
1068 assert res.almost_all.response_time_p66.ToTimedelta() == timedelta(hours=35)
1070 # send a request and back date it by 2 hours
1071 host_request_4 = api.CreateHostRequest(
1072 requests_pb2.CreateHostRequestReq(
1073 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
1074 )
1075 ).host_request_id
1076 with session_scope() as session:
1077 session.execute(
1078 select(Message)
1079 .where(Message.conversation_id == host_request_4)
1080 .where(Message.message_type == MessageType.chat_created)
1081 ).scalar_one().time = now() - timedelta(hours=2)
1082 refresh_materialized_view(session, "user_response_rates")
1084 # send a request and back date it by 4 hours
1085 host_request_5 = api.CreateHostRequest(
1086 requests_pb2.CreateHostRequestReq(
1087 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
1088 )
1089 ).host_request_id
1090 with session_scope() as session:
1091 session.execute(
1092 select(Message)
1093 .where(Message.conversation_id == host_request_5)
1094 .where(Message.message_type == MessageType.chat_created)
1095 ).scalar_one().time = now() - timedelta(hours=4)
1096 refresh_materialized_view(session, "user_response_rates")
1098 # now some w p33 = 35h
1099 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1100 assert res.HasField("some")
1101 assert res.some.response_time_p33.ToTimedelta() == timedelta(hours=35)
1103 with requests_session(token2) as api:
1104 # accept host req
1105 api.RespondHostRequest(
1106 requests_pb2.RespondHostRequestReq(
1107 host_request_id=host_request_5,
1108 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1109 text="Accepting host request",
1110 )
1111 )
1113 with session_scope() as session:
1114 refresh_materialized_view(session, "user_response_rates")
1116 with requests_session(token1) as api:
1117 # now most w p33 = 34h, p66 = 36h
1118 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1119 assert res.HasField("most")
1120 assert res.most.response_time_p33.ToTimedelta() == timedelta(hours=34)
1121 assert res.most.response_time_p66.ToTimedelta() == timedelta(hours=36)
1123 with requests_session(token2) as api:
1124 # accept host req
1125 api.RespondHostRequest(
1126 requests_pb2.RespondHostRequestReq(
1127 host_request_id=host_request_4,
1128 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1129 text="Accepting host request",
1130 )
1131 )
1133 with session_scope() as session:
1134 refresh_materialized_view(session, "user_response_rates")
1136 with requests_session(token1) as api:
1137 # now most w p33 = 4h, p66 = 35h
1138 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1139 assert res.HasField("almost_all")
1140 assert res.almost_all.response_time_p33.ToTimedelta() == timedelta(hours=4)
1141 assert res.almost_all.response_time_p66.ToTimedelta() == timedelta(hours=35)
1144def test_request_notifications(db, push_collector):
1145 host, host_token = generate_user(complete_profile=True)
1146 surfer, surfer_token = generate_user(complete_profile=True)
1148 today_plus_2 = (today() + timedelta(days=2)).isoformat()
1149 today_plus_3 = (today() + timedelta(days=3)).isoformat()
1151 with requests_session(surfer_token) as api:
1152 with mock_notification_email() as mock:
1153 hr_id = api.CreateHostRequest(
1154 requests_pb2.CreateHostRequestReq(
1155 host_user_id=host.id,
1156 from_date=today_plus_2,
1157 to_date=today_plus_3,
1158 text="can i stay plz",
1159 )
1160 ).host_request_id
1162 mock.assert_called_once()
1163 e = email_fields(mock)
1164 assert e.recipient == host.email
1165 assert "host request" in e.subject.lower()
1166 assert host.name in e.plain
1167 assert host.name in e.html
1168 assert "quick decline" in e.plain.lower(), e.plain
1169 assert "quick decline" in e.html.lower()
1170 assert surfer.name in e.plain
1171 assert surfer.name in e.html
1172 assert v2date(today_plus_2, host) in e.plain
1173 assert v2date(today_plus_2, host) in e.html
1174 assert v2date(today_plus_3, host) in e.plain
1175 assert v2date(today_plus_3, host) in e.html
1176 assert "http://localhost:5001/img/thumbnail/" not in e.plain
1177 assert "http://localhost:5001/img/thumbnail/" in e.html
1178 assert f"http://localhost:3000/messages/request/{hr_id}" in e.plain
1179 assert f"http://localhost:3000/messages/request/{hr_id}" in e.html
1181 push_collector.assert_user_has_single_matching(
1182 host.id,
1183 title=f"{surfer.name} sent you a host request",
1184 )
1186 with requests_session(host_token) as api:
1187 with mock_notification_email() as mock:
1188 api.RespondHostRequest(
1189 requests_pb2.RespondHostRequestReq(
1190 host_request_id=hr_id,
1191 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1192 text="Accepting host request",
1193 )
1194 )
1196 e = email_fields(mock)
1197 assert e.recipient == surfer.email
1198 assert "host request" in e.subject.lower()
1199 assert host.name in e.plain
1200 assert host.name in e.html
1201 assert surfer.name in e.plain
1202 assert surfer.name in e.html
1203 assert v2date(today_plus_2, surfer) in e.plain
1204 assert v2date(today_plus_2, surfer) in e.html
1205 assert v2date(today_plus_3, surfer) in e.plain
1206 assert v2date(today_plus_3, surfer) in e.html
1207 assert "http://localhost:5001/img/thumbnail/" not in e.plain
1208 assert "http://localhost:5001/img/thumbnail/" in e.html
1209 assert f"http://localhost:3000/messages/request/{hr_id}" in e.plain
1210 assert f"http://localhost:3000/messages/request/{hr_id}" in e.html
1212 push_collector.assert_user_has_single_matching(
1213 surfer.id,
1214 title=f"{host.name} accepted your host request",
1215 )
1218def test_quick_decline(db, push_collector):
1219 host, host_token = generate_user(complete_profile=True)
1220 surfer, surfer_token = generate_user(complete_profile=True)
1222 today_plus_2 = (today() + timedelta(days=2)).isoformat()
1223 today_plus_3 = (today() + timedelta(days=3)).isoformat()
1225 with requests_session(surfer_token) as api:
1226 with mock_notification_email() as mock:
1227 hr_id = api.CreateHostRequest(
1228 requests_pb2.CreateHostRequestReq(
1229 host_user_id=host.id,
1230 from_date=today_plus_2,
1231 to_date=today_plus_3,
1232 text="can i stay plz",
1233 )
1234 ).host_request_id
1236 mock.assert_called_once()
1237 e = email_fields(mock)
1238 assert e.recipient == host.email
1239 assert "host request" in e.subject.lower()
1240 assert host.name in e.plain
1241 assert host.name in e.html
1242 assert "quick decline" in e.plain.lower(), e.plain
1243 assert "quick decline" in e.html.lower()
1244 assert surfer.name in e.plain
1245 assert surfer.name in e.html
1246 assert v2date(today_plus_2, host) in e.plain
1247 assert v2date(today_plus_2, host) in e.html
1248 assert v2date(today_plus_3, host) in e.plain
1249 assert v2date(today_plus_3, host) in e.html
1250 assert "http://localhost:5001/img/thumbnail/" not in e.plain
1251 assert "http://localhost:5001/img/thumbnail/" in e.html
1252 assert f"http://localhost:3000/messages/request/{hr_id}" in e.plain
1253 assert f"http://localhost:3000/messages/request/{hr_id}" in e.html
1255 push_collector.assert_user_has_single_matching(
1256 host.id,
1257 title=f"{surfer.name} sent you a host request",
1258 )
1260 # very ugly
1261 # http://localhost:3000/quick-link?payload=CAEiGAoOZnJpZW5kX3JlcXVlc3QSBmFjY2VwdA==&sig=BQdk024NTATm8zlR0krSXTBhP5U9TlFv7VhJeIHZtUg=
1262 for link in re.findall(r'<a href="(.*?)"', email_fields(mock).html):
1263 if "payload" not in link:
1264 continue
1265 print(link)
1266 url_parts = urlparse(link)
1267 params = parse_qs(url_parts.query)
1268 print(params["payload"][0])
1269 payload = unsubscribe_pb2.UnsubscribePayload.FromString(b64decode(params["payload"][0]))
1270 if payload.HasField("host_request_quick_decline"):
1271 with auth_api_session() as (auth_api, metadata_interceptor):
1272 res = auth_api.Unsubscribe(
1273 auth_pb2.UnsubscribeReq(
1274 payload=b64decode(params["payload"][0]),
1275 sig=b64decode(params["sig"][0]),
1276 )
1277 )
1278 break
1279 else:
1280 raise Exception("Didn't find link")
1282 with requests_session(surfer_token) as api:
1283 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=hr_id))
1284 assert res.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
1287def test_host_req_feedback(db):
1288 host, host_token = generate_user(complete_profile=True)
1289 host2, host2_token = generate_user(complete_profile=True)
1290 host3, host3_token = generate_user(complete_profile=True)
1291 surfer, surfer_token = generate_user(complete_profile=True)
1293 today_plus_2 = (today() + timedelta(days=2)).isoformat()
1294 today_plus_3 = (today() + timedelta(days=3)).isoformat()
1296 with requests_session(surfer_token) as api:
1297 hr_id = api.CreateHostRequest(
1298 requests_pb2.CreateHostRequestReq(
1299 host_user_id=host.id,
1300 from_date=today_plus_2,
1301 to_date=today_plus_3,
1302 text="can i stay plz",
1303 )
1304 ).host_request_id
1305 hr2_id = api.CreateHostRequest(
1306 requests_pb2.CreateHostRequestReq(
1307 host_user_id=host2.id,
1308 from_date=today_plus_2,
1309 to_date=today_plus_3,
1310 text="can i stay plz",
1311 )
1312 ).host_request_id
1313 hr3_id = api.CreateHostRequest(
1314 requests_pb2.CreateHostRequestReq(
1315 host_user_id=host3.id,
1316 from_date=today_plus_2,
1317 to_date=today_plus_3,
1318 text="can i stay plz",
1319 )
1320 ).host_request_id
1322 with requests_session(host_token) as api:
1323 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=hr_id))
1324 assert not res.need_host_request_feedback
1326 api.RespondHostRequest(
1327 requests_pb2.RespondHostRequestReq(
1328 host_request_id=hr_id,
1329 status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED,
1330 )
1331 )
1333 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=hr_id))
1334 assert res.need_host_request_feedback
1336 # surfer can't leave feedback
1337 with requests_session(surfer_token) as api:
1338 with pytest.raises(grpc.RpcError) as e:
1339 api.SendHostRequestFeedback(
1340 requests_pb2.SendHostRequestFeedbackReq(
1341 host_request_id=hr_id,
1342 )
1343 )
1344 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1345 assert e.value.details() == errors.HOST_REQUEST_NOT_FOUND
1347 with requests_session(host_token) as api:
1348 api.SendHostRequestFeedback(
1349 requests_pb2.SendHostRequestFeedbackReq(
1350 host_request_id=hr_id,
1351 host_request_quality=requests_pb2.HOST_REQUEST_QUALITY_LOW,
1352 )
1353 )
1354 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=hr_id))
1355 assert not res.need_host_request_feedback
1357 # can't leave it twice
1358 with requests_session(host_token) as api:
1359 with pytest.raises(grpc.RpcError) as e:
1360 api.SendHostRequestFeedback(
1361 requests_pb2.SendHostRequestFeedbackReq(
1362 host_request_id=hr_id,
1363 )
1364 )
1365 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
1366 assert e.value.details() == errors.ALREADY_LEFT_HOST_REQUEST_FEEDBACK
1368 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=hr_id))
1369 assert not res.need_host_request_feedback
1371 with requests_session(host2_token) as api:
1372 api.RespondHostRequest(
1373 requests_pb2.RespondHostRequestReq(
1374 host_request_id=hr2_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
1375 )
1376 )
1377 # can't leave feedback on the wrong one
1378 with pytest.raises(grpc.RpcError) as e:
1379 api.SendHostRequestFeedback(
1380 requests_pb2.SendHostRequestFeedbackReq(
1381 host_request_id=hr_id,
1382 )
1383 )
1384 assert e.value.code() == grpc.StatusCode.NOT_FOUND
1385 assert e.value.details() == errors.HOST_REQUEST_NOT_FOUND
1387 # null feedback is still feedback
1388 api.SendHostRequestFeedback(requests_pb2.SendHostRequestFeedbackReq(host_request_id=hr2_id))
1390 with requests_session(host3_token) as api:
1391 api.RespondHostRequest(
1392 requests_pb2.RespondHostRequestReq(
1393 host_request_id=hr3_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
1394 )
1395 )
1397 api.SendHostRequestFeedback(
1398 requests_pb2.SendHostRequestFeedbackReq(host_request_id=hr3_id, decline_reason="bad req")
1399 )