Coverage for src/tests/test_requests.py: 100%
530 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-04-16 15:13 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-04-16 15:13 +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 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=conversation_id))
519 # 9 including initial message
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, last_message_id=res.messages[2].message_id, number=6
536 )
537 )
538 assert res.no_more
539 assert len(res.messages) == 6
540 assert res.messages[0].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
541 assert res.messages[0].WhichOneof("content") == "host_request_status_changed"
542 assert res.messages[1].text.text == "Test request 1 message 3"
543 assert res.messages[2].text.text == "Test request 1 message 2"
544 assert res.messages[3].text.text == "Test request 1 message 1"
545 assert res.messages[4].text.text == "Test request 1"
546 assert res.messages[5].WhichOneof("content") == "chat_created"
549def test_SendHostRequestMessage(db):
550 user1, token1 = generate_user()
551 user2, token2 = generate_user()
552 user3, token3 = generate_user()
553 today_plus_2 = (today() + timedelta(days=2)).isoformat()
554 today_plus_3 = (today() + timedelta(days=3)).isoformat()
555 with requests_session(token1) as api:
556 host_request_id = api.CreateHostRequest(
557 requests_pb2.CreateHostRequestReq(
558 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request 1"
559 )
560 ).host_request_id
562 with pytest.raises(grpc.RpcError) as e:
563 api.SendHostRequestMessage(
564 requests_pb2.SendHostRequestMessageReq(host_request_id=999, text="Test message 1")
565 )
566 assert e.value.code() == grpc.StatusCode.NOT_FOUND
568 with pytest.raises(grpc.RpcError) as e:
569 api.SendHostRequestMessage(requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text=""))
570 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
571 assert e.value.details() == errors.INVALID_MESSAGE
573 api.SendHostRequestMessage(
574 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
575 )
576 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
577 assert res.messages[0].text.text == "Test message 1"
578 assert res.messages[0].author_user_id == user1.id
580 with requests_session(token3) as api:
581 # other user can't send
582 with pytest.raises(grpc.RpcError) as e:
583 api.SendHostRequestMessage(
584 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
585 )
586 assert e.value.code() == grpc.StatusCode.NOT_FOUND
587 assert e.value.details() == errors.HOST_REQUEST_NOT_FOUND
589 with requests_session(token2) as api:
590 api.SendHostRequestMessage(
591 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
592 )
593 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
594 # including 2 for creation control message and message
595 assert len(res.messages) == 4
596 assert res.messages[0].text.text == "Test message 2"
597 assert res.messages[0].author_user_id == user2.id
599 # can't send messages to a rejected, confirmed or cancelled request, but can for accepted
600 api.RespondHostRequest(
601 requests_pb2.RespondHostRequestReq(
602 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_REJECTED
603 )
604 )
605 with pytest.raises(grpc.RpcError) as e:
606 api.SendHostRequestMessage(
607 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
608 )
609 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
610 assert e.value.details() == errors.HOST_REQUEST_CLOSED
612 api.RespondHostRequest(
613 requests_pb2.RespondHostRequestReq(
614 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED
615 )
616 )
618 with requests_session(token1) as api:
619 api.RespondHostRequest(
620 requests_pb2.RespondHostRequestReq(
621 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CONFIRMED
622 )
623 )
624 api.SendHostRequestMessage(
625 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
626 )
628 api.RespondHostRequest(
629 requests_pb2.RespondHostRequestReq(
630 host_request_id=host_request_id, status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
631 )
632 )
633 with pytest.raises(grpc.RpcError) as e:
634 api.SendHostRequestMessage(
635 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 3")
636 )
637 assert e.value.code() == grpc.StatusCode.PERMISSION_DENIED
638 assert e.value.details() == errors.HOST_REQUEST_CLOSED
641def test_get_updates(db):
642 user1, token1 = generate_user()
643 user2, token2 = generate_user()
644 user3, token3 = generate_user()
645 today_plus_2 = (today() + timedelta(days=2)).isoformat()
646 today_plus_3 = (today() + timedelta(days=3)).isoformat()
647 with requests_session(token1) as api:
648 host_request_id = api.CreateHostRequest(
649 requests_pb2.CreateHostRequestReq(
650 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
651 )
652 ).host_request_id
654 api.SendHostRequestMessage(
655 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
656 )
657 api.SendHostRequestMessage(
658 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
659 )
660 api.RespondHostRequest(
661 requests_pb2.RespondHostRequestReq(
662 host_request_id=host_request_id,
663 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
664 text="Test message 3",
665 )
666 )
668 api.CreateHostRequest(
669 requests_pb2.CreateHostRequestReq(
670 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 4"
671 )
672 )
674 res = api.GetHostRequestMessages(requests_pb2.GetHostRequestMessagesReq(host_request_id=host_request_id))
675 assert len(res.messages) == 6
676 assert res.messages[0].text.text == "Test message 3"
677 assert res.messages[1].host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
678 assert res.messages[2].text.text == "Test message 2"
679 assert res.messages[3].text.text == "Test message 1"
680 assert res.messages[4].text.text == "Test message 0"
681 message_id_3 = res.messages[0].message_id
682 message_id_cancel = res.messages[1].message_id
683 message_id_2 = res.messages[2].message_id
684 message_id_1 = res.messages[3].message_id
685 message_id_0 = res.messages[4].message_id
687 with pytest.raises(grpc.RpcError) as e:
688 api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=0))
689 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
691 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1))
692 assert res.no_more
693 assert len(res.updates) == 5
694 assert res.updates[0].message.text.text == "Test message 2"
695 assert (
696 res.updates[1].message.host_request_status_changed.status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
697 )
698 assert res.updates[1].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
699 assert res.updates[2].message.text.text == "Test message 3"
700 assert res.updates[3].message.WhichOneof("content") == "chat_created"
701 assert res.updates[3].status == conversations_pb2.HOST_REQUEST_STATUS_PENDING
702 assert res.updates[4].message.text.text == "Test message 4"
704 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1, number=1))
705 assert not res.no_more
706 assert len(res.updates) == 1
707 assert res.updates[0].message.text.text == "Test message 2"
708 assert res.updates[0].status == conversations_pb2.HOST_REQUEST_STATUS_CANCELLED
710 with requests_session(token3) as api:
711 # other user can't access
712 res = api.GetHostRequestUpdates(requests_pb2.GetHostRequestUpdatesReq(newest_message_id=message_id_1))
713 assert len(res.updates) == 0
716def test_mark_last_seen(db):
717 user1, token1 = generate_user()
718 user2, token2 = generate_user()
719 user3, token3 = generate_user()
720 today_plus_2 = (today() + timedelta(days=2)).isoformat()
721 today_plus_3 = (today() + timedelta(days=3)).isoformat()
722 with requests_session(token1) as api:
723 host_request_id = api.CreateHostRequest(
724 requests_pb2.CreateHostRequestReq(
725 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0"
726 )
727 ).host_request_id
729 host_request_id_2 = api.CreateHostRequest(
730 requests_pb2.CreateHostRequestReq(
731 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test message 0a"
732 )
733 ).host_request_id
735 api.SendHostRequestMessage(
736 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 1")
737 )
738 api.SendHostRequestMessage(
739 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id, text="Test message 2")
740 )
741 api.RespondHostRequest(
742 requests_pb2.RespondHostRequestReq(
743 host_request_id=host_request_id,
744 status=conversations_pb2.HOST_REQUEST_STATUS_CANCELLED,
745 text="Test message 3",
746 )
747 )
749 # test Ping unseen host request count, should be automarked after sending
750 with api_session(token1) as api:
751 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 0
752 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
754 with api_session(token2) as api:
755 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 2
756 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
758 with requests_session(token2) as api:
759 assert api.ListHostRequests(requests_pb2.ListHostRequestsReq()).host_requests[0].last_seen_message_id == 0
761 api.MarkLastSeenHostRequest(
762 requests_pb2.MarkLastSeenHostRequestReq(host_request_id=host_request_id, last_seen_message_id=3)
763 )
765 assert api.ListHostRequests(requests_pb2.ListHostRequestsReq()).host_requests[0].last_seen_message_id == 3
767 with pytest.raises(grpc.RpcError) as e:
768 api.MarkLastSeenHostRequest(
769 requests_pb2.MarkLastSeenHostRequestReq(host_request_id=host_request_id, last_seen_message_id=1)
770 )
771 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
772 assert e.value.details() == errors.CANT_UNSEE_MESSAGES
774 # this will be used to test sent request notifications
775 host_request_id_3 = api.CreateHostRequest(
776 requests_pb2.CreateHostRequestReq(
777 host_user_id=user1.id, from_date=today_plus_2, to_date=today_plus_3, text="Another test request"
778 )
779 ).host_request_id
781 # this should make id_2 all read
782 api.SendHostRequestMessage(
783 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id_2, text="Test")
784 )
786 with api_session(token2) as api:
787 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 1
788 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 0
790 # make sure sent and received count for unseen notifications
791 with requests_session(token1) as api:
792 api.SendHostRequestMessage(
793 requests_pb2.SendHostRequestMessageReq(host_request_id=host_request_id_3, text="Test message")
794 )
796 with api_session(token2) as api:
797 assert api.Ping(api_pb2.PingReq()).unseen_received_host_request_count == 1
798 assert api.Ping(api_pb2.PingReq()).unseen_sent_host_request_count == 1
801def test_response_rate(db):
802 user1, token1 = generate_user()
803 user2, token2 = generate_user()
804 user3, token3 = generate_user(delete_user=True)
806 today_plus_2 = (today() + timedelta(days=2)).isoformat()
807 today_plus_3 = (today() + timedelta(days=3)).isoformat()
809 with session_scope() as session:
810 refresh_materialized_view(session, "user_response_rates")
812 with requests_session(token1) as api:
813 # deleted: not found
814 with pytest.raises(grpc.RpcError) as e:
815 api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user3.id))
816 assert e.value.code() == grpc.StatusCode.NOT_FOUND
817 assert e.value.details() == errors.USER_NOT_FOUND
819 # no requests: insufficient
820 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
821 assert res.HasField("insufficient_data")
823 # send a request and back date it by 36 hours
824 host_request_1 = api.CreateHostRequest(
825 requests_pb2.CreateHostRequestReq(
826 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
827 )
828 ).host_request_id
829 with session_scope() as session:
830 session.execute(
831 select(Message)
832 .where(Message.conversation_id == host_request_1)
833 .where(Message.message_type == MessageType.chat_created)
834 ).scalar_one().time = now() - timedelta(hours=36)
835 refresh_materialized_view(session, "user_response_rates")
837 # still insufficient
838 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
839 assert res.HasField("insufficient_data")
841 # send a request and back date it by 35 hours
842 host_request_2 = api.CreateHostRequest(
843 requests_pb2.CreateHostRequestReq(
844 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
845 )
846 ).host_request_id
847 with session_scope() as session:
848 session.execute(
849 select(Message)
850 .where(Message.conversation_id == host_request_2)
851 .where(Message.message_type == MessageType.chat_created)
852 ).scalar_one().time = now() - timedelta(hours=35)
853 refresh_materialized_view(session, "user_response_rates")
855 # still insufficient
856 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
857 assert res.HasField("insufficient_data")
859 # send a request and back date it by 34 hours
860 host_request_3 = api.CreateHostRequest(
861 requests_pb2.CreateHostRequestReq(
862 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
863 )
864 ).host_request_id
865 with session_scope() as session:
866 session.execute(
867 select(Message)
868 .where(Message.conversation_id == host_request_3)
869 .where(Message.message_type == MessageType.chat_created)
870 ).scalar_one().time = now() - timedelta(hours=34)
871 refresh_materialized_view(session, "user_response_rates")
873 # now low
874 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
875 assert res.HasField("low")
877 with requests_session(token2) as api:
878 # accept a host req
879 api.RespondHostRequest(
880 requests_pb2.RespondHostRequestReq(
881 host_request_id=host_request_2,
882 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
883 text="Accepting host request",
884 )
885 )
887 with session_scope() as session:
888 refresh_materialized_view(session, "user_response_rates")
890 with requests_session(token1) as api:
891 # now some w p33 = 35h
892 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
893 assert res.HasField("some")
894 assert res.some.response_time_p33.ToTimedelta() == timedelta(hours=35)
896 with requests_session(token2) as api:
897 # accept another host req
898 api.RespondHostRequest(
899 requests_pb2.RespondHostRequestReq(
900 host_request_id=host_request_3,
901 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
902 text="Accepting host request",
903 )
904 )
906 with session_scope() as session:
907 refresh_materialized_view(session, "user_response_rates")
909 with requests_session(token1) as api:
910 # now most w p33 = 34h, p66 = 35h
911 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
912 assert res.HasField("most")
913 assert res.most.response_time_p33.ToTimedelta() == timedelta(hours=34)
914 assert res.most.response_time_p66.ToTimedelta() == timedelta(hours=35)
916 with requests_session(token2) as api:
917 # accept last host req
918 api.RespondHostRequest(
919 requests_pb2.RespondHostRequestReq(
920 host_request_id=host_request_1,
921 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
922 text="Accepting host request",
923 )
924 )
926 with session_scope() as session:
927 refresh_materialized_view(session, "user_response_rates")
929 with requests_session(token1) as api:
930 # now all w p33 = 34h, p66 = 35h
931 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
932 assert res.HasField("almost_all")
933 assert res.almost_all.response_time_p33.ToTimedelta() == timedelta(hours=34)
934 assert res.almost_all.response_time_p66.ToTimedelta() == timedelta(hours=35)
936 # send a request and back date it by 2 hours
937 host_request_4 = api.CreateHostRequest(
938 requests_pb2.CreateHostRequestReq(
939 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
940 )
941 ).host_request_id
942 with session_scope() as session:
943 session.execute(
944 select(Message)
945 .where(Message.conversation_id == host_request_4)
946 .where(Message.message_type == MessageType.chat_created)
947 ).scalar_one().time = now() - timedelta(hours=2)
948 refresh_materialized_view(session, "user_response_rates")
950 # send a request and back date it by 4 hours
951 host_request_5 = api.CreateHostRequest(
952 requests_pb2.CreateHostRequestReq(
953 host_user_id=user2.id, from_date=today_plus_2, to_date=today_plus_3, text="Test request"
954 )
955 ).host_request_id
956 with session_scope() as session:
957 session.execute(
958 select(Message)
959 .where(Message.conversation_id == host_request_5)
960 .where(Message.message_type == MessageType.chat_created)
961 ).scalar_one().time = now() - timedelta(hours=4)
962 refresh_materialized_view(session, "user_response_rates")
964 # now some w p33 = 35h
965 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
966 assert res.HasField("some")
967 assert res.some.response_time_p33.ToTimedelta() == timedelta(hours=35)
969 with requests_session(token2) as api:
970 # accept host req
971 api.RespondHostRequest(
972 requests_pb2.RespondHostRequestReq(
973 host_request_id=host_request_5,
974 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
975 text="Accepting host request",
976 )
977 )
979 with session_scope() as session:
980 refresh_materialized_view(session, "user_response_rates")
982 with requests_session(token1) as api:
983 # now most w p33 = 34h, p66 = 36h
984 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
985 assert res.HasField("most")
986 assert res.most.response_time_p33.ToTimedelta() == timedelta(hours=34)
987 assert res.most.response_time_p66.ToTimedelta() == timedelta(hours=36)
989 with requests_session(token2) as api:
990 # accept host req
991 api.RespondHostRequest(
992 requests_pb2.RespondHostRequestReq(
993 host_request_id=host_request_4,
994 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
995 text="Accepting host request",
996 )
997 )
999 with session_scope() as session:
1000 refresh_materialized_view(session, "user_response_rates")
1002 with requests_session(token1) as api:
1003 # now most w p33 = 4h, p66 = 35h
1004 res = api.GetResponseRate(requests_pb2.GetResponseRateReq(user_id=user2.id))
1005 assert res.HasField("almost_all")
1006 assert res.almost_all.response_time_p33.ToTimedelta() == timedelta(hours=4)
1007 assert res.almost_all.response_time_p66.ToTimedelta() == timedelta(hours=35)
1010def test_request_notifications(db, push_collector):
1011 host, host_token = generate_user(complete_profile=True)
1012 surfer, surfer_token = generate_user(complete_profile=True)
1014 today_plus_2 = (today() + timedelta(days=2)).isoformat()
1015 today_plus_3 = (today() + timedelta(days=3)).isoformat()
1017 with requests_session(surfer_token) as api:
1018 with mock_notification_email() as mock:
1019 hr_id = api.CreateHostRequest(
1020 requests_pb2.CreateHostRequestReq(
1021 host_user_id=host.id,
1022 from_date=today_plus_2,
1023 to_date=today_plus_3,
1024 text="can i stay plz",
1025 )
1026 ).host_request_id
1028 mock.assert_called_once()
1029 e = email_fields(mock)
1030 assert e.recipient == host.email
1031 assert "host request" in e.subject.lower()
1032 assert host.name in e.plain
1033 assert host.name in e.html
1034 assert surfer.name in e.plain
1035 assert surfer.name in e.html
1036 assert v2date(today_plus_2, host) in e.plain
1037 assert v2date(today_plus_2, host) in e.html
1038 assert v2date(today_plus_3, host) in e.plain
1039 assert v2date(today_plus_3, host) in e.html
1040 assert "http://localhost:5001/img/thumbnail/" not in e.plain
1041 assert "http://localhost:5001/img/thumbnail/" in e.html
1042 assert f"http://localhost:3000/messages/request/{hr_id}" in e.plain
1043 assert f"http://localhost:3000/messages/request/{hr_id}" in e.html
1045 push_collector.assert_user_has_single_matching(
1046 host.id,
1047 title=f"{surfer.name} sent you a host request",
1048 )
1050 with requests_session(host_token) as api:
1051 with mock_notification_email() as mock:
1052 api.RespondHostRequest(
1053 requests_pb2.RespondHostRequestReq(
1054 host_request_id=hr_id,
1055 status=conversations_pb2.HOST_REQUEST_STATUS_ACCEPTED,
1056 text="Accepting host request",
1057 )
1058 )
1060 e = email_fields(mock)
1061 assert e.recipient == surfer.email
1062 assert "host request" in e.subject.lower()
1063 assert host.name in e.plain
1064 assert host.name in e.html
1065 assert surfer.name in e.plain
1066 assert surfer.name in e.html
1067 assert v2date(today_plus_2, surfer) in e.plain
1068 assert v2date(today_plus_2, surfer) in e.html
1069 assert v2date(today_plus_3, surfer) in e.plain
1070 assert v2date(today_plus_3, surfer) in e.html
1071 assert "http://localhost:5001/img/thumbnail/" not in e.plain
1072 assert "http://localhost:5001/img/thumbnail/" in e.html
1073 assert f"http://localhost:3000/messages/request/{hr_id}" in e.plain
1074 assert f"http://localhost:3000/messages/request/{hr_id}" in e.html
1076 push_collector.assert_user_has_single_matching(
1077 surfer.id,
1078 title=f"{host.name} accepted your host request",
1079 )