Coverage for src/tests/test_requests.py: 100%
547 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-07-02 02:47 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-07-02 02:47 +0000
1from datetime import timedelta
3import grpc
4import pytest
5from sqlalchemy.sql import select
7from couchers import errors
8from couchers.db import session_scope
9from couchers.materialized_views import refresh_materialized_view
10from couchers.models import Message, MessageType
11from couchers.templates.v2 import v2date
12from couchers.utils import now, today
13from proto import api_pb2, conversations_pb2, requests_pb2
14from tests.test_fixtures import ( # noqa
15 api_session,
16 db,
17 email_fields,
18 generate_user,
19 mock_notification_email,
20 push_collector,
21 requests_session,
22 testconfig,
23)
26@pytest.fixture(autouse=True)
27def _(testconfig):
28 pass
31def test_create_request(db):
32 user1, token1 = generate_user()
33 user2, token2 = generate_user()
34 today_plus_2 = (today() + timedelta(days=2)).isoformat()
35 today_plus_3 = (today() + timedelta(days=3)).isoformat()
36 today_minus_2 = (today() - timedelta(days=2)).isoformat()
37 today_minus_3 = (today() - timedelta(days=3)).isoformat()
38 with requests_session(token1) as api:
39 with pytest.raises(grpc.RpcError) as e:
40 api.CreateHostRequest(
41 requests_pb2.CreateHostRequestReq(
42 host_user_id=user1.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
43 )
44 )
45 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
46 assert e.value.details() == errors.CANT_REQUEST_SELF
48 with pytest.raises(grpc.RpcError) as e:
49 api.CreateHostRequest(
50 requests_pb2.CreateHostRequestReq(
51 host_user_id=999, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
52 )
53 )
54 assert e.value.code() == grpc.StatusCode.NOT_FOUND
55 assert e.value.details() == errors.USER_NOT_FOUND
57 with pytest.raises(grpc.RpcError) as e:
58 api.CreateHostRequest(
59 requests_pb2.CreateHostRequestReq(
60 host_user_id=user2.id, from_date=today_plus_3, to_date=today_plus_2, text="Test request"
61 )
62 )
63 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
64 assert e.value.details() == errors.DATE_FROM_AFTER_TO
66 with pytest.raises(grpc.RpcError) as e:
67 api.CreateHostRequest(
68 requests_pb2.CreateHostRequestReq(
69 host_user_id=user2.id, from_date=today_minus_3, to_date=today_plus_2, text="Test request"
70 )
71 )
72 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
73 assert e.value.details() == errors.DATE_FROM_BEFORE_TODAY
75 with pytest.raises(grpc.RpcError) as e:
76 api.CreateHostRequest(
77 requests_pb2.CreateHostRequestReq(
78 host_user_id=user2.id, from_date=today_plus_2, to_date=today_minus_2, text="Test request"
79 )
80 )
81 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
82 assert e.value.details() == errors.DATE_FROM_AFTER_TO
84 with pytest.raises(grpc.RpcError) as e:
85 api.CreateHostRequest(
86 requests_pb2.CreateHostRequestReq(
87 host_user_id=user2.id, from_date="2020-00-06", to_date=today_minus_2, text="Test request"
88 )
89 )
90 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
91 assert e.value.details() == errors.INVALID_DATE
93 res = api.CreateHostRequest(
94 requests_pb2.CreateHostRequestReq(
95 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
96 )
97 )
98 assert (
99 api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True))
100 .host_requests[0]
101 .latest_message.text.text
102 == "Test request"
103 )
105 today_ = today()
106 today_plus_one_year = today_ + timedelta(days=365)
107 today_plus_one_year_plus_2 = (today_plus_one_year + timedelta(days=2)).isoformat()
108 today_plus_one_year_plus_3 = (today_plus_one_year + timedelta(days=3)).isoformat()
109 with pytest.raises(grpc.RpcError) as e:
110 api.CreateHostRequest(
111 requests_pb2.CreateHostRequestReq(
112 host_user_id=user2.id,
113 from_date=today_plus_one_year_plus_2,
114 to_date=today_plus_one_year_plus_3,
115 text="Test from date after one year",
116 )
117 )
118 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
119 assert e.value.details() == errors.DATE_FROM_AFTER_ONE_YEAR
121 with pytest.raises(grpc.RpcError) as e:
122 api.CreateHostRequest(
123 requests_pb2.CreateHostRequestReq(
124 host_user_id=user2.id,
125 from_date=today_plus_2,
126 to_date=today_plus_one_year_plus_3,
127 text="Test to date one year after from date",
128 )
129 )
130 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
131 assert e.value.details() == errors.DATE_TO_AFTER_ONE_YEAR
134def test_create_request_incomplete_profile(db):
135 user1, token1 = generate_user(complete_profile=False)
136 user2, _ = generate_user()
137 today_plus_2 = (today() + timedelta(days=2)).isoformat()
138 today_plus_3 = (today() + timedelta(days=3)).isoformat()
139 with requests_session(token1) as api:
140 with pytest.raises(grpc.RpcError) as e:
141 api.CreateHostRequest(
142 requests_pb2.CreateHostRequestReq(
143 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
144 )
145 )
146 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
147 assert e.value.details() == errors.INCOMPLETE_PROFILE_SEND_REQUEST
150def add_message(db, text, author_id, conversation_id):
151 with session_scope() as session:
152 message = Message(
153 conversation_id=conversation_id, author_id=author_id, text=text, message_type=MessageType.text
154 )
156 session.add(message)
159def test_GetHostRequest(db):
160 user1, token1 = generate_user()
161 user2, token2 = generate_user()
162 user3, token3 = generate_user()
163 today_plus_2 = (today() + timedelta(days=2)).isoformat()
164 today_plus_3 = (today() + timedelta(days=3)).isoformat()
165 with requests_session(token1) as api:
166 host_request_id = api.CreateHostRequest(
167 requests_pb2.CreateHostRequestReq(
168 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
169 )
170 ).host_request_id
172 with pytest.raises(grpc.RpcError) as e:
173 api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=999))
174 assert e.value.code() == grpc.StatusCode.NOT_FOUND
175 assert e.value.details() == errors.HOST_REQUEST_NOT_FOUND
177 api.SendHostRequestMessage(
178 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
179 )
181 res = api.GetHostRequest(requests_pb2.GetHostRequestReq(host_request_id=host_request_id))
182 assert res.latest_message.text.text == "Test message 1"
185def test_ListHostRequests(db):
186 user1, token1 = generate_user()
187 user2, token2 = generate_user()
188 user3, token3 = generate_user()
189 today_plus_2 = (today() + timedelta(days=2)).isoformat()
190 today_plus_3 = (today() + timedelta(days=3)).isoformat()
191 with requests_session(token1) as api:
192 host_request_1 = api.CreateHostRequest(
193 requests_pb2.CreateHostRequestReq(
194 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
195 )
196 ).host_request_id
198 host_request_2 = api.CreateHostRequest(
199 requests_pb2.CreateHostRequestReq(
200 host_user_id=user3.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 2"
201 )
202 ).host_request_id
204 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True))
205 assert res.no_more
206 assert len(res.host_requests) == 2
208 with requests_session(token2) as api:
209 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
210 assert res.no_more
211 assert len(res.host_requests) == 1
212 assert res.host_requests[0].latest_message.text.text == "Test request 1"
213 assert res.host_requests[0].surfer_user_id == user1.id
214 assert res.host_requests[0].host_user_id == user2.id
215 assert res.host_requests[0].status == conversations_pb2.HOST_REQUEST_STATUS_PENDING
217 add_message(db, "Test request 1 message 1", user2.id, host_request_1)
218 add_message(db, "Test request 1 message 2", user2.id, host_request_1)
219 add_message(db, "Test request 1 message 3", user2.id, host_request_1)
221 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
222 assert res.host_requests[0].latest_message.text.text == "Test request 1 message 3"
224 api.CreateHostRequest(
225 requests_pb2.CreateHostRequestReq(
226 host_user_id=user1.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 3"
227 )
228 )
230 add_message(db, "Test request 2 message 1", user1.id, host_request_2)
231 add_message(db, "Test request 2 message 2", user3.id, host_request_2)
233 with requests_session(token3) as api:
234 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
235 assert res.no_more
236 assert len(res.host_requests) == 1
237 assert res.host_requests[0].latest_message.text.text == "Test request 2 message 2"
239 with requests_session(token1) as api:
240 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
241 assert len(res.host_requests) == 1
243 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq())
244 assert len(res.host_requests) == 3
247def test_ListHostRequests_pagination_regression(db):
248 """
249 ListHostRequests was skipping a request when getting multiple pages
250 """
251 user1, token1 = generate_user()
252 user2, token2 = generate_user()
253 today_plus_2 = (today() + timedelta(days=2)).isoformat()
254 today_plus_3 = (today() + timedelta(days=3)).isoformat()
255 with requests_session(token1) as api:
256 host_request_1 = api.CreateHostRequest(
257 requests_pb2.CreateHostRequestReq(
258 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
259 )
260 ).host_request_id
262 host_request_2 = api.CreateHostRequest(
263 requests_pb2.CreateHostRequestReq(
264 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 2"
265 )
266 ).host_request_id
268 host_request_3 = api.CreateHostRequest(
269 requests_pb2.CreateHostRequestReq(
270 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 3"
271 )
272 ).host_request_id
274 with requests_session(token2) as api:
275 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
276 assert res.no_more
277 assert len(res.host_requests) == 3
278 assert res.host_requests[0].latest_message.text.text == "Test request 3"
279 assert res.host_requests[1].latest_message.text.text == "Test request 2"
280 assert res.host_requests[2].latest_message.text.text == "Test request 1"
282 with requests_session(token2) as api:
283 api.RespondHostRequest(
284 requests_pb2.RespondHostRequestReq(
285 host_request_id=host_request_2,
286 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
287 text="Accepting host request 2",
288 )
289 )
290 api.RespondHostRequest(
291 requests_pb2.RespondHostRequestReq(
292 host_request_id=host_request_1,
293 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
294 text="Accepting host request 1",
295 )
296 )
297 api.RespondHostRequest(
298 requests_pb2.RespondHostRequestReq(
299 host_request_id=host_request_3,
300 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
301 text="Accepting host request 3",
302 )
303 )
305 with requests_session(token2) as api:
306 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
307 assert res.no_more
308 assert len(res.host_requests) == 3
309 assert res.host_requests[0].latest_message.text.text == "Accepting host request 3"
310 assert res.host_requests[1].latest_message.text.text == "Accepting host request 1"
311 assert res.host_requests[2].latest_message.text.text == "Accepting host request 2"
313 with requests_session(token2) as api:
314 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True, number=1))
315 assert not res.no_more
316 assert len(res.host_requests) == 1
317 assert res.host_requests[0].latest_message.text.text == "Accepting host request 3"
318 res = api.ListHostRequests(
319 requests_pb2.ListHostRequestsReq(only_received=True, number=1, last_request_id=res.last_request_id)
320 )
321 assert not res.no_more
322 assert len(res.host_requests) == 1
323 assert res.host_requests[0].latest_message.text.text == "Accepting host request 1"
324 res = api.ListHostRequests(
325 requests_pb2.ListHostRequestsReq(only_received=True, number=1, last_request_id=res.last_request_id)
326 )
327 assert res.no_more
328 assert len(res.host_requests) == 1
329 assert res.host_requests[0].latest_message.text.text == "Accepting host request 2"
332def test_ListHostRequests_active_filter(db):
333 user1, token1 = generate_user()
334 user2, token2 = generate_user()
335 today_plus_2 = (today() + timedelta(days=2)).isoformat()
336 today_plus_3 = (today() + timedelta(days=3)).isoformat()
338 with requests_session(token1) as api:
339 request_id = api.CreateHostRequest(
340 requests_pb2.CreateHostRequestReq(
341 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
342 )
343 ).host_request_id
344 api.RespondHostRequest(
345 requests_pb2.RespondHostRequestReq(
346 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
347 )
348 )
350 with requests_session(token2) as api:
351 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_received=True))
352 assert len(res.host_requests) == 1
353 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_active=True))
354 assert len(res.host_requests) == 0
357def test_RespondHostRequests(db):
358 user1, token1 = generate_user()
359 user2, token2 = generate_user()
360 user3, token3 = generate_user()
361 today_plus_2 = (today() + timedelta(days=2)).isoformat()
362 today_plus_3 = (today() + timedelta(days=3)).isoformat()
364 with requests_session(token1) as api:
365 request_id = api.CreateHostRequest(
366 requests_pb2.CreateHostRequestReq(
367 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
368 )
369 ).host_request_id
371 # another user can't access
372 with requests_session(token3) as api:
373 with pytest.raises(grpc.RpcError) as e:
374 api.RespondHostRequest(
375 requests_pb2.RespondHostRequestReq(
376 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
377 )
378 )
379 assert e.value.code() == grpc.StatusCode.NOT_FOUND
380 assert e.value.details() == errors.HOST_REQUEST_NOT_FOUND
382 with requests_session(token1) as api:
383 with pytest.raises(grpc.RpcError) as e:
384 api.RespondHostRequest(
385 requests_pb2.RespondHostRequestReq(
386 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
387 )
388 )
389 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
390 assert e.value.details() == errors.NOT_THE_HOST
392 with requests_session(token2) as api:
393 # non existing id
394 with pytest.raises(grpc.RpcError) as e:
395 api.RespondHostRequest(
396 requests_pb2.RespondHostRequestReq(
397 host_request_id=9999, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
398 )
399 )
400 assert e.value.code() == grpc.StatusCode.NOT_FOUND
402 # host can't confirm or cancel (host should accept/reject)
403 with pytest.raises(grpc.RpcError) as e:
404 api.RespondHostRequest(
405 requests_pb2.RespondHostRequestReq(
406 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
407 )
408 )
409 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
410 assert e.value.details() == errors.INVALID_HOST_REQUEST_STATUS
411 with pytest.raises(grpc.RpcError) as e:
412 api.RespondHostRequest(
413 requests_pb2.RespondHostRequestReq(
414 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
415 )
416 )
417 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
418 assert e.value.details() == errors.INVALID_HOST_REQUEST_STATUS
420 api.RespondHostRequest(
421 requests_pb2.RespondHostRequestReq(
422 host_request_id=request_id,
423 status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED,
424 text="Test rejection message",
425 )
426 )
427 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=request_id))
428 assert res.messages[0].text.text == "Test rejection message"
429 assert res.messages[1].WhichOneof("content") == "host_request_status_changed"
430 assert res.messages[1].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
431 # should be able to move from rejected -> accepted
432 api.RespondHostRequest(
433 requests_pb2.RespondHostRequestReq(
434 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
435 )
436 )
438 with requests_session(token1) as api:
439 # can't make pending
440 with pytest.raises(grpc.RpcError) as e:
441 api.RespondHostRequest(
442 requests_pb2.RespondHostRequestReq(
443 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_PENDING
444 )
445 )
446 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
447 assert e.value.details() == errors.INVALID_HOST_REQUEST_STATUS
449 # can confirm then cancel
450 api.RespondHostRequest(
451 requests_pb2.RespondHostRequestReq(
452 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
453 )
454 )
456 api.RespondHostRequest(
457 requests_pb2.RespondHostRequestReq(
458 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
459 )
460 )
462 # can't confirm after having cancelled
463 with pytest.raises(grpc.RpcError) as e:
464 api.RespondHostRequest(
465 requests_pb2.RespondHostRequestReq(
466 host_request_id=request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
467 )
468 )
469 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
470 assert e.value.details() == errors.INVALID_HOST_REQUEST_STATUS
472 # at this point there should be 7 messages
473 # 2 for creation, 2 for the status change with message, 3 for the other status changed
474 with requests_session(token1) as api:
475 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=request_id))
476 assert len(res.messages) == 7
477 assert res.messages[0].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
478 assert res.messages[1].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
479 assert res.messages[2].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
480 assert res.messages[4].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
481 assert res.messages[6].WhichOneof("content") == "chat_created"
484def test_get_host_request_messages(db):
485 user1, token1 = generate_user()
486 user2, token2 = generate_user()
487 today_plus_2 = (today() + timedelta(days=2)).isoformat()
488 today_plus_3 = (today() + timedelta(days=3)).isoformat()
489 with requests_session(token1) as api:
490 res = api.CreateHostRequest(
491 requests_pb2.CreateHostRequestReq(
492 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
493 )
494 )
495 conversation_id = res.host_request_id
497 add_message(db, "Test request 1 message 1", user1.id, conversation_id)
498 add_message(db, "Test request 1 message 2", user1.id, conversation_id)
499 add_message(db, "Test request 1 message 3", user1.id, conversation_id)
501 with requests_session(token2) as api:
502 api.RespondHostRequest(
503 requests_pb2.RespondHostRequestReq(
504 host_request_id=conversation_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
505 )
506 )
508 add_message(db, "Test request 1 message 4", user2.id, conversation_id)
509 add_message(db, "Test request 1 message 5", user2.id, conversation_id)
511 api.RespondHostRequest(
512 requests_pb2.RespondHostRequestReq(
513 host_request_id=conversation_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
514 )
515 )
517 with requests_session(token1) as api:
518 # 9 including initial message
519 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=conversation_id))
520 assert len(res.messages) == 9
521 assert res.no_more
523 res = api.GetHostRequestMessages(
524 requests_pb2.GetHostRequestMessagesReq(host_request_id=conversation_id, number=3)
525 )
526 assert not res.no_more
527 assert len(res.messages) == 3
528 assert res.messages[0].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_REJECTED
529 assert res.messages[0].WhichOneof("content") == "host_request_status_changed"
530 assert res.messages[1].text.text == "Test request 1 message 5"
531 assert res.messages[2].text.text == "Test request 1 message 4"
533 res = api.GetHostRequestMessages(
534 requests_pb2.GetHostRequestMessagesReq(
535 host_request_id=conversation_id,
536 last_message_id=res.messages[2].message_id,
537 number=6,
538 )
539 )
540 assert res.no_more
541 assert len(res.messages) == 6
542 assert res.messages[0].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
543 assert res.messages[0].WhichOneof("content") == "host_request_status_changed"
544 assert res.messages[1].text.text == "Test request 1 message 3"
545 assert res.messages[2].text.text == "Test request 1 message 2"
546 assert res.messages[3].text.text == "Test request 1 message 1"
547 assert res.messages[4].text.text == "Test request 1"
548 assert res.messages[5].WhichOneof("content") == "chat_created"
551def test_SendHostRequestMessage(db):
552 user1, token1 = generate_user()
553 user2, token2 = generate_user()
554 user3, token3 = generate_user()
555 today_plus_2 = (today() + timedelta(days=2)).isoformat()
556 today_plus_3 = (today() + timedelta(days=3)).isoformat()
557 with requests_session(token1) as api:
558 host_request_id = api.CreateHostRequest(
559 requests_pb2.CreateHostRequestReq(
560 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
561 )
562 ).host_request_id
564 with pytest.raises(grpc.RpcError) as e:
565 api.SendHostRequestMessage(
566 requests_pb2.SendHostRequestMessageReq(host_request_id=999, text="Test message 1")
567 )
568 assert e.value.code() == grpc.StatusCode.NOT_FOUND
570 with pytest.raises(grpc.RpcError) as e:
571 api.SendHostRequestMessage(requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text=""))
572 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
573 assert e.value.details() == errors.INVALID_MESSAGE
575 api.SendHostRequestMessage(
576 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
577 )
578 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
579 assert res.messages[0].text.text == "Test message 1"
580 assert res.messages[0].author_user_id == user1.id
582 with requests_session(token3) as api:
583 # other user can't send
584 with pytest.raises(grpc.RpcError) as e:
585 api.SendHostRequestMessage(
586 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
587 )
588 assert e.value.code() == grpc.StatusCode.NOT_FOUND
589 assert e.value.details() == errors.HOST_REQUEST_NOT_FOUND
591 with requests_session(token2) as api:
592 api.SendHostRequestMessage(
593 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
594 )
595 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
596 # including 2 for creation control message and message
597 assert len(res.messages) == 4
598 assert res.messages[0].text.text == "Test message 2"
599 assert res.messages[0].author_user_id == user2.id
601 # can't send messages to a rejected, confirmed or cancelled request, but can for accepted
602 api.RespondHostRequest(
603 requests_pb2.RespondHostRequestReq(
604 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
605 )
606 )
607 with pytest.raises(grpc.RpcError) as e:
608 api.SendHostRequestMessage(
609 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
610 )
611 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
612 assert e.value.details() == errors.HOST_REQUEST_CLOSED
614 api.RespondHostRequest(
615 requests_pb2.RespondHostRequestReq(
616 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
617 )
618 )
620 with requests_session(token1) as api:
621 api.RespondHostRequest(
622 requests_pb2.RespondHostRequestReq(
623 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
624 )
625 )
626 api.SendHostRequestMessage(
627 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
628 )
630 api.RespondHostRequest(
631 requests_pb2.RespondHostRequestReq(
632 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
633 )
634 )
635 with pytest.raises(grpc.RpcError) as e:
636 api.SendHostRequestMessage(
637 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
638 )
639 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
640 assert e.value.details() == errors.HOST_REQUEST_CLOSED
643def test_get_updates(db):
644 user1, token1 = generate_user()
645 user2, token2 = generate_user()
646 user3, token3 = generate_user()
647 today_plus_2 = (today() + timedelta(days=2)).isoformat()
648 today_plus_3 = (today() + timedelta(days=3)).isoformat()
649 with requests_session(token1) as api:
650 host_request_id = api.CreateHostRequest(
651 requests_pb2.CreateHostRequestReq(
652 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
653 )
654 ).host_request_id
656 api.SendHostRequestMessage(
657 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
658 )
659 api.SendHostRequestMessage(
660 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
661 )
662 api.RespondHostRequest(
663 requests_pb2.RespondHostRequestReq(
664 host_request_id=host_request_id,
665 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
666 text="Test message 3",
667 )
668 )
670 api.CreateHostRequest(
671 requests_pb2.CreateHostRequestReq(
672 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 4"
673 )
674 )
676 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
677 assert len(res.messages) == 6
678 assert res.messages[0].text.text == "Test message 3"
679 assert res.messages[1].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
680 assert res.messages[2].text.text == "Test message 2"
681 assert res.messages[3].text.text == "Test message 1"
682 assert res.messages[4].text.text == "Test message 0"
683 message_id_3 = res.messages[0].message_id
684 message_id_cancel = res.messages[1].message_id
685 message_id_2 = res.messages[2].message_id
686 message_id_1 = res.messages[3].message_id
687 message_id_0 = res.messages[4].message_id
689 with pytest.raises(grpc.RpcError) as e:
690 api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=0))
691 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
693 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1))
694 assert res.no_more
695 assert len(res.updates) == 5
696 assert res.updates[0].message.text.text == "Test message 2"
697 assert (
698 res.updates[1].message.host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
699 )
700 assert res.updates[1].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
701 assert res.updates[2].message.text.text == "Test message 3"
702 assert res.updates[3].message.WhichOneof("content") == "chat_created"
703 assert res.updates[3].status == conversations_pb2.HOST_REQUEST_STATUS_PENDING
704 assert res.updates[4].message.text.text == "Test message 4"
706 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1, number=1))
707 assert not res.no_more
708 assert len(res.updates) == 1
709 assert res.updates[0].message.text.text == "Test message 2"
710 assert res.updates[0].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
712 with requests_session(token3) as api:
713 # other user can't access
714 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1))
715 assert len(res.updates) == 0
718def test_archive_host_request(db):
719 user1, token1 = generate_user()
720 user2, token2 = generate_user()
722 today_plus_2 = (today() + timedelta(days=2)).isoformat()
723 today_plus_3 = (today() + timedelta(days=3)).isoformat()
725 with requests_session(token1) as api:
726 host_request_id = api.CreateHostRequest(
727 requests_pb2.CreateHostRequestReq(
728 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
729 )
730 ).host_request_id
732 api.SendHostRequestMessage(
733 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
734 )
735 api.SendHostRequestMessage(
736 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
737 )
738 # happy path archiving host request
739 with requests_session(token1) as api:
740 api.RespondHostRequest(
741 requests_pb2.RespondHostRequestReq(
742 host_request_id=host_request_id,
743 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
744 text="Test message 3",
745 )
746 )
747 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_sent=True))
748 assert len(res.host_requests) == 1
749 assert res.host_requests[0].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
750 api.SetHostRequestArchiveStatus(
751 requests_pb2.SetHostRequestArchiveStatusReq(host_request_id=host_request_id, is_archived=True)
752 )
753 res = api.ListHostRequests(requests_pb2.ListHostRequestsReq(only_archived=True))
754 assert len(res.host_requests) == 1
757def test_mark_last_seen(db):
758 user1, token1 = generate_user()
759 user2, token2 = generate_user()
760 user3, token3 = generate_user()
761 today_plus_2 = (today() + timedelta(days=2)).isoformat()
762 today_plus_3 = (today() + timedelta(days=3)).isoformat()
763 with requests_session(token1) as api:
764 host_request_id = api.CreateHostRequest(
765 requests_pb2.CreateHostRequestReq(
766 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
767 )
768 ).host_request_id
770 host_request_id_2 = api.CreateHostRequest(
771 requests_pb2.CreateHostRequestReq(
772 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0a"
773 )
774 ).host_request_id
776 api.SendHostRequestMessage(
777 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
778 )
779 api.SendHostRequestMessage(
780 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
781 )
782 api.RespondHostRequest(
783 requests_pb2.RespondHostRequestReq(
784 host_request_id=host_request_id,
785 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
786 text="Test message 3",
787 )
788 )
790 # test Ping unseen host request count, should be automarked after sending
791 with api_session(token1) as api:
792 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 0
793 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
795 with api_session(token2) as api:
796 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 2
797 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
799 with requests_session(token2) as api:
800 assert api.ListHostRequests(requests_pb2.ListHostRequestsReq()).host_requests[0].last_seen_message_id == 0
802 api.MarkLastSeenHostRequest(
803 requests_pb2.MarkLastSeenHostRequestReq(host_request_id=host_request_id, last_seen_message_id=3)
804 )
806 assert api.ListHostRequests(requests_pb2.ListHostRequestsReq()).host_requests[0].last_seen_message_id == 3
808 with pytest.raises(grpc.RpcError) as e:
809 api.MarkLastSeenHostRequest(
810 requests_pb2.MarkLastSeenHostRequestReq(host_request_id=host_request_id, last_seen_message_id=1)
811 )
812 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
813 assert e.value.details() == errors.CANT_UNSEE_MESSAGES
815 # this will be used to test sent request notifications
816 host_request_id_3 = api.CreateHostRequest(
817 requests_pb2.CreateHostRequestReq(
818 host_user_id=user1.id, from_date=today_plus_2, to_date=today_plus_3, text="Another test request"
819 )
820 ).host_request_id
822 # this should make id_2 all read
823 api.SendHostRequestMessage(
824 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id_2, text="Test")
825 )
827 with api_session(token2) as api:
828 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 1
829 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
831 # make sure sent and received count for unseen notifications
832 with requests_session(token1) as api:
833 api.SendHostRequestMessage(
834 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id_3, text="Test message")
835 )
837 with api_session(token2) as api:
838 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 1
839 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 1
842def test_response_rate(db):
843 user1, token1 = generate_user()
844 user2, token2 = generate_user()
845 user3, token3 = generate_user(delete_user=True)
847 today_plus_2 = (today() + timedelta(days=2)).isoformat()
848 today_plus_3 = (today() + timedelta(days=3)).isoformat()
850 with session_scope() as session:
851 refresh_materialized_view(session, "user_response_rates")
853 with requests_session(token1) as api:
854 # deleted: not found
855 with pytest.raises(grpc.RpcError) as e:
856 api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user3.id))
857 assert e.value.code() == grpc.StatusCode.NOT_FOUND
858 assert e.value.details() == errors.USER_NOT_FOUND
860 # no requests: insufficient
861 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
862 assert res.HasField("insufficient_data")
864 # send a request and back date it by 36 hours
865 host_request_1 = api.CreateHostRequest(
866 requests_pb2.CreateHostRequestReq(
867 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
868 )
869 ).host_request_id
870 with session_scope() as session:
871 session.execute(
872 select(Message)
873 .where(Message.conversation_id == host_request_1)
874 .where(Message.message_type == MessageType.chat_created)
875 ).scalar_one().time = now() - timedelta(hours=36)
876 refresh_materialized_view(session, "user_response_rates")
878 # still insufficient
879 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
880 assert res.HasField("insufficient_data")
882 # send a request and back date it by 35 hours
883 host_request_2 = api.CreateHostRequest(
884 requests_pb2.CreateHostRequestReq(
885 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
886 )
887 ).host_request_id
888 with session_scope() as session:
889 session.execute(
890 select(Message)
891 .where(Message.conversation_id == host_request_2)
892 .where(Message.message_type == MessageType.chat_created)
893 ).scalar_one().time = now() - timedelta(hours=35)
894 refresh_materialized_view(session, "user_response_rates")
896 # still insufficient
897 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
898 assert res.HasField("insufficient_data")
900 # send a request and back date it by 34 hours
901 host_request_3 = api.CreateHostRequest(
902 requests_pb2.CreateHostRequestReq(
903 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
904 )
905 ).host_request_id
906 with session_scope() as session:
907 session.execute(
908 select(Message)
909 .where(Message.conversation_id == host_request_3)
910 .where(Message.message_type == MessageType.chat_created)
911 ).scalar_one().time = now() - timedelta(hours=34)
912 refresh_materialized_view(session, "user_response_rates")
914 # now low
915 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
916 assert res.HasField("low")
918 with requests_session(token2) as api:
919 # accept a host req
920 api.RespondHostRequest(
921 requests_pb2.RespondHostRequestReq(
922 host_request_id=host_request_2,
923 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
924 text="Accepting host request",
925 )
926 )
928 with session_scope() as session:
929 refresh_materialized_view(session, "user_response_rates")
931 with requests_session(token1) as api:
932 # now some w p33 = 35h
933 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
934 assert res.HasField("some")
935 assert res.some.response_time_p33.ToTimedelta() == timedelta(hours=35)
937 with requests_session(token2) as api:
938 # accept another host req
939 api.RespondHostRequest(
940 requests_pb2.RespondHostRequestReq(
941 host_request_id=host_request_3,
942 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
943 text="Accepting host request",
944 )
945 )
947 with session_scope() as session:
948 refresh_materialized_view(session, "user_response_rates")
950 with requests_session(token1) as api:
951 # now most w p33 = 34h, p66 = 35h
952 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
953 assert res.HasField("most")
954 assert res.most.response_time_p33.ToTimedelta() == timedelta(hours=34)
955 assert res.most.response_time_p66.ToTimedelta() == timedelta(hours=35)
957 with requests_session(token2) as api:
958 # accept last host req
959 api.RespondHostRequest(
960 requests_pb2.RespondHostRequestReq(
961 host_request_id=host_request_1,
962 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
963 text="Accepting host request",
964 )
965 )
967 with session_scope() as session:
968 refresh_materialized_view(session, "user_response_rates")
970 with requests_session(token1) as api:
971 # now all w p33 = 34h, p66 = 35h
972 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
973 assert res.HasField("almost_all")
974 assert res.almost_all.response_time_p33.ToTimedelta() == timedelta(hours=34)
975 assert res.almost_all.response_time_p66.ToTimedelta() == timedelta(hours=35)
977 # send a request and back date it by 2 hours
978 host_request_4 = 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_4)
987 .where(Message.message_type == MessageType.chat_created)
988 ).scalar_one().time = now() - timedelta(hours=2)
989 refresh_materialized_view(session, "user_response_rates")
991 # send a request and back date it by 4 hours
992 host_request_5 = api.CreateHostRequest(
993 requests_pb2.CreateHostRequestReq(
994 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
995 )
996 ).host_request_id
997 with session_scope() as session:
998 session.execute(
999 select(Message)
1000 .where(Message.conversation_id == host_request_5)
1001 .where(Message.message_type == MessageType.chat_created)
1002 ).scalar_one().time = now() - timedelta(hours=4)
1003 refresh_materialized_view(session, "user_response_rates")
1005 # now some w p33 = 35h
1006 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1007 assert res.HasField("some")
1008 assert res.some.response_time_p33.ToTimedelta() == timedelta(hours=35)
1010 with requests_session(token2) as api:
1011 # accept host req
1012 api.RespondHostRequest(
1013 requests_pb2.RespondHostRequestReq(
1014 host_request_id=host_request_5,
1015 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1016 text="Accepting host request",
1017 )
1018 )
1020 with session_scope() as session:
1021 refresh_materialized_view(session, "user_response_rates")
1023 with requests_session(token1) as api:
1024 # now most w p33 = 34h, p66 = 36h
1025 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1026 assert res.HasField("most")
1027 assert res.most.response_time_p33.ToTimedelta() == timedelta(hours=34)
1028 assert res.most.response_time_p66.ToTimedelta() == timedelta(hours=36)
1030 with requests_session(token2) as api:
1031 # accept host req
1032 api.RespondHostRequest(
1033 requests_pb2.RespondHostRequestReq(
1034 host_request_id=host_request_4,
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 = 4h, p66 = 35h
1045 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1046 assert res.HasField("almost_all")
1047 assert res.almost_all.response_time_p33.ToTimedelta() == timedelta(hours=4)
1048 assert res.almost_all.response_time_p66.ToTimedelta() == timedelta(hours=35)
1051def test_request_notifications(db, push_collector):
1052 host, host_token = generate_user(complete_profile=True)
1053 surfer, surfer_token = generate_user(complete_profile=True)
1055 today_plus_2 = (today() + timedelta(days=2)).isoformat()
1056 today_plus_3 = (today() + timedelta(days=3)).isoformat()
1058 with requests_session(surfer_token) as api:
1059 with mock_notification_email() as mock:
1060 hr_id = api.CreateHostRequest(
1061 requests_pb2.CreateHostRequestReq(
1062 host_user_id=host.id,
1063 from_date=today_plus_2,
1064 to_date=today_plus_3,
1065 text="can i stay plz",
1066 )
1067 ).host_request_id
1069 mock.assert_called_once()
1070 e = email_fields(mock)
1071 assert e.recipient == host.email
1072 assert "host request" in e.subject.lower()
1073 assert host.name in e.plain
1074 assert host.name in e.html
1075 assert surfer.name in e.plain
1076 assert surfer.name in e.html
1077 assert v2date(today_plus_2, host) in e.plain
1078 assert v2date(today_plus_2, host) in e.html
1079 assert v2date(today_plus_3, host) in e.plain
1080 assert v2date(today_plus_3, host) in e.html
1081 assert "http://localhost:5001/img/thumbnail/" not in e.plain
1082 assert "http://localhost:5001/img/thumbnail/" in e.html
1083 assert f"http://localhost:3000/messages/request/{hr_id}" in e.plain
1084 assert f"http://localhost:3000/messages/request/{hr_id}" in e.html
1086 push_collector.assert_user_has_single_matching(
1087 host.id,
1088 title=f"{surfer.name} sent you a host request",
1089 )
1091 with requests_session(host_token) as api:
1092 with mock_notification_email() as mock:
1093 api.RespondHostRequest(
1094 requests_pb2.RespondHostRequestReq(
1095 host_request_id=hr_id,
1096 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1097 text="Accepting host request",
1098 )
1099 )
1101 e = email_fields(mock)
1102 assert e.recipient == surfer.email
1103 assert "host request" in e.subject.lower()
1104 assert host.name in e.plain
1105 assert host.name in e.html
1106 assert surfer.name in e.plain
1107 assert surfer.name in e.html
1108 assert v2date(today_plus_2, surfer) in e.plain
1109 assert v2date(today_plus_2, surfer) in e.html
1110 assert v2date(today_plus_3, surfer) in e.plain
1111 assert v2date(today_plus_3, surfer) in e.html
1112 assert "http://localhost:5001/img/thumbnail/" not in e.plain
1113 assert "http://localhost:5001/img/thumbnail/" in e.html
1114 assert f"http://localhost:3000/messages/request/{hr_id}" in e.plain
1115 assert f"http://localhost:3000/messages/request/{hr_id}" in e.html
1117 push_collector.assert_user_has_single_matching(
1118 surfer.id,
1119 title=f"{host.name} accepted your host request",
1120 )