Coverage for src/tests/test_api.py: 100%
700 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-11-21 04:21 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-11-21 04:21 +0000
1from datetime import timedelta
3import grpc
4import pytest
5from google.protobuf import empty_pb2, wrappers_pb2
7from couchers import errors
8from couchers.db import session_scope
9from couchers.jobs.handlers import update_badges
10from couchers.materialized_views import refresh_materialized_views_rapid
11from couchers.models import FriendRelationship, FriendStatus
12from couchers.sql import couchers_select as select
13from couchers.utils import create_coordinate, to_aware_datetime
14from proto import api_pb2, jail_pb2, notifications_pb2
15from tests.test_fixtures import ( # noqa
16 api_session,
17 blocking_session,
18 db,
19 email_fields,
20 generate_user,
21 make_friends,
22 make_user_block,
23 make_user_invisible,
24 mock_notification_email,
25 notifications_session,
26 push_collector,
27 real_api_session,
28 real_jail_session,
29 testconfig,
30)
33@pytest.fixture(autouse=True)
34def _(testconfig):
35 pass
38def test_ping(db):
39 user, token = generate_user()
41 with real_api_session(token) as api:
42 res = api.Ping(api_pb2.PingReq())
44 assert res.user.user_id == user.id
45 assert res.user.username == user.username
46 assert res.user.name == user.name
47 assert res.user.city == user.city
48 assert res.user.hometown == user.hometown
49 assert res.user.verification == 0.0
50 assert res.user.community_standing == user.community_standing
51 assert res.user.num_references == 0
52 assert res.user.gender == user.gender
53 assert res.user.pronouns == user.pronouns
54 assert res.user.age == user.age
56 assert (res.user.lat, res.user.lng) == (user.coordinates or (0, 0))
58 # the joined time is fuzzed
59 # but shouldn't be before actual joined time, or more than one hour behind
60 assert user.joined - timedelta(hours=1) <= to_aware_datetime(res.user.joined) <= user.joined
61 # same for last_active
62 assert user.last_active - timedelta(hours=1) <= to_aware_datetime(res.user.last_active) <= user.last_active
64 assert res.user.hosting_status == api_pb2.HOSTING_STATUS_CANT_HOST
65 assert res.user.meetup_status == api_pb2.MEETUP_STATUS_OPEN_TO_MEETUP
67 assert res.user.occupation == user.occupation
68 assert res.user.education == user.education
69 assert res.user.about_me == user.about_me
70 assert res.user.my_travels == user.my_travels
71 assert res.user.things_i_like == user.things_i_like
72 assert {language_ability.code for language_ability in res.user.language_abilities} == {"fin", "fra"}
73 assert res.user.about_place == user.about_place
74 assert res.user.regions_visited == ["FIN", "REU", "CHE"] # Tests alphabetization by region name
75 assert res.user.regions_lived == ["EST", "FRA", "ESP"] # Ditto
76 assert res.user.additional_information == user.additional_information
78 assert res.user.friends == api_pb2.User.FriendshipStatus.NA
79 assert not res.user.HasField("pending_friend_request")
82def test_coords(db):
83 # make them have not added a location
84 user1, token1 = generate_user(geom=None, geom_radius=None)
85 user2, token2 = generate_user()
87 with api_session(token2) as api:
88 res = api.Ping(api_pb2.PingReq())
89 assert res.user.city == user2.city
90 lat, lng = user2.coordinates or (0, 0)
91 assert res.user.lat == lat
92 assert res.user.lng == lng
93 assert res.user.radius == user2.geom_radius
95 with api_session(token2) as api:
96 res = api.GetUser(api_pb2.GetUserReq(user=user1.username))
97 assert res.city == user1.city
98 assert res.lat == 0.0
99 assert res.lng == 0.0
100 assert res.radius == 0.0
102 # Check coordinate wrapping
103 user3, token3 = generate_user(geom=create_coordinate(40.0, -180.5))
104 user4, token4 = generate_user(geom=create_coordinate(40.0, 20.0))
105 user5, token5 = generate_user(geom=create_coordinate(90.5, 20.0))
107 with api_session(token3) as api:
108 res = api.GetUser(api_pb2.GetUserReq(user=user3.username))
109 assert res.lat == 40.0
110 assert res.lng == 179.5
112 with api_session(token4) as api:
113 res = api.GetUser(api_pb2.GetUserReq(user=user4.username))
114 assert res.lat == 40.0
115 assert res.lng == 20.0
117 # PostGIS does not wrap longitude for latitude overflow
118 with api_session(token5) as api:
119 res = api.GetUser(api_pb2.GetUserReq(user=user5.username))
120 assert res.lat == 89.5
121 assert res.lng == 20.0
123 with real_jail_session(token1) as jail:
124 res = jail.JailInfo(empty_pb2.Empty())
125 assert res.jailed
126 assert res.has_not_added_location
128 res = jail.SetLocation(
129 jail_pb2.SetLocationReq(
130 city="New York City",
131 lat=40.7812,
132 lng=-73.9647,
133 radius=250,
134 )
135 )
137 assert not res.jailed
138 assert not res.has_not_added_location
140 res = jail.JailInfo(empty_pb2.Empty())
141 assert not res.jailed
142 assert not res.has_not_added_location
144 with api_session(token2) as api:
145 res = api.GetUser(api_pb2.GetUserReq(user=user1.username))
146 assert res.city == "New York City"
147 assert res.lat == 40.7812
148 assert res.lng == -73.9647
149 assert res.radius == 250
152def test_get_user(db):
153 user1, token1 = generate_user()
154 user2, token2 = generate_user()
156 with api_session(token1) as api:
157 res = api.GetUser(api_pb2.GetUserReq(user=user2.username))
158 assert res.user_id == user2.id
159 assert res.username == user2.username
160 assert res.name == user2.name
162 with api_session(token1) as api:
163 res = api.GetUser(api_pb2.GetUserReq(user=str(user2.id)))
164 assert res.user_id == user2.id
165 assert res.username == user2.username
166 assert res.name == user2.name
169def test_lite_coords(db):
170 # make them have not added a location
171 user1, token1 = generate_user(geom=None, geom_radius=None)
172 user2, token2 = generate_user()
174 refresh_materialized_views_rapid(None)
176 with api_session(token2) as api:
177 res = api.Ping(api_pb2.PingReq())
178 assert res.user.city == user2.city
179 lat, lng = user2.coordinates or (0, 0)
180 assert res.user.lat == lat
181 assert res.user.lng == lng
182 assert res.user.radius == user2.geom_radius
184 with api_session(token2) as api:
185 res = api.GetLiteUser(api_pb2.GetLiteUserReq(user=user1.username))
186 assert res.city == user1.city
187 assert res.lat == 0.0
188 assert res.lng == 0.0
189 assert res.radius == 0.0
191 # Check coordinate wrapping
192 user3, token3 = generate_user(geom=create_coordinate(40.0, -180.5))
193 user4, token4 = generate_user(geom=create_coordinate(40.0, 20.0))
194 user5, token5 = generate_user(geom=create_coordinate(90.5, 20.0))
196 refresh_materialized_views_rapid(None)
198 with api_session(token3) as api:
199 res = api.GetLiteUser(api_pb2.GetLiteUserReq(user=user3.username))
200 assert res.lat == 40.0
201 assert res.lng == 179.5
203 with api_session(token4) as api:
204 res = api.GetLiteUser(api_pb2.GetLiteUserReq(user=user4.username))
205 assert res.lat == 40.0
206 assert res.lng == 20.0
208 # PostGIS does not wrap longitude for latitude overflow
209 with api_session(token5) as api:
210 res = api.GetLiteUser(api_pb2.GetLiteUserReq(user=user5.username))
211 assert res.lat == 89.5
212 assert res.lng == 20.0
214 with real_jail_session(token1) as jail:
215 res = jail.JailInfo(empty_pb2.Empty())
216 assert res.jailed
217 assert res.has_not_added_location
219 res = jail.SetLocation(
220 jail_pb2.SetLocationReq(
221 city="New York City",
222 lat=40.7812,
223 lng=-73.9647,
224 radius=250,
225 )
226 )
228 assert not res.jailed
229 assert not res.has_not_added_location
231 res = jail.JailInfo(empty_pb2.Empty())
232 assert not res.jailed
233 assert not res.has_not_added_location
235 refresh_materialized_views_rapid(None)
237 with api_session(token2) as api:
238 res = api.GetLiteUser(api_pb2.GetLiteUserReq(user=user1.username))
239 assert res.city == "New York City"
240 assert res.lat == 40.7812
241 assert res.lng == -73.9647
242 assert res.radius == 250
245def test_lite_get_user(db):
246 user1, token1 = generate_user()
247 user2, token2 = generate_user()
249 refresh_materialized_views_rapid(None)
251 with api_session(token1) as api:
252 res = api.GetLiteUser(api_pb2.GetLiteUserReq(user=user2.username))
253 assert res.user_id == user2.id
254 assert res.username == user2.username
255 assert res.name == user2.name
257 with api_session(token1) as api:
258 res = api.GetLiteUser(api_pb2.GetLiteUserReq(user=str(user2.id)))
259 assert res.user_id == user2.id
260 assert res.username == user2.username
261 assert res.name == user2.name
264def test_GetLiteUsers(db):
265 user1, token1 = generate_user()
266 user2, _ = generate_user()
267 user3, _ = generate_user()
268 user4, _ = generate_user()
269 user5, _ = generate_user()
270 user6, _ = generate_user()
272 make_user_block(user4, user1)
274 refresh_materialized_views_rapid(None)
276 with api_session(token1) as api:
277 res = api.GetLiteUsers(
278 api_pb2.GetLiteUsersReq(
279 users=[
280 user1.username,
281 str(user1.id),
282 "nonexistent",
283 str(user2.id),
284 "9994",
285 user6.username,
286 str(user5.id),
287 "notreal",
288 user4.username,
289 ]
290 )
291 )
293 assert len(res.responses) == 9
294 assert res.responses[0].query == user1.username
295 assert res.responses[0].user.user_id == user1.id
297 assert res.responses[1].query == str(user1.id)
298 assert res.responses[1].user.user_id == user1.id
300 assert res.responses[2].query == "nonexistent"
301 assert res.responses[2].not_found
303 assert res.responses[3].query == str(user2.id)
304 assert res.responses[3].user.user_id == user2.id
306 assert res.responses[4].query == "9994"
307 assert res.responses[4].not_found
309 assert res.responses[5].query == user6.username
310 assert res.responses[5].user.user_id == user6.id
312 assert res.responses[6].query == str(user5.id)
313 assert res.responses[6].user.user_id == user5.id
315 assert res.responses[7].query == "notreal"
316 assert res.responses[7].not_found
318 # blocked
319 assert res.responses[8].query == user4.username
320 assert res.responses[8].not_found
322 with api_session(token1) as api:
323 with pytest.raises(grpc.RpcError) as e:
324 api.GetLiteUsers(api_pb2.GetLiteUsersReq(users=201 * [user1.username]))
325 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
326 assert e.value.details() == errors.REQUESTED_TOO_MANY_USERS
329def test_update_profile(db):
330 user, token = generate_user()
332 with api_session(token) as api:
333 with pytest.raises(grpc.RpcError) as e:
334 api.UpdateProfile(api_pb2.UpdateProfileReq(name=wrappers_pb2.StringValue(value=" ")))
335 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
336 assert e.value.details() == errors.INVALID_NAME
338 with pytest.raises(grpc.RpcError) as e:
339 api.UpdateProfile(
340 api_pb2.UpdateProfileReq(lat=wrappers_pb2.DoubleValue(value=0), lng=wrappers_pb2.DoubleValue(value=0))
341 )
342 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
343 assert e.value.details() == errors.INVALID_COORDINATE
345 with pytest.raises(grpc.RpcError) as e:
346 api.UpdateProfile(
347 api_pb2.UpdateProfileReq(regions_visited=api_pb2.RepeatedStringValue(value=["United States"]))
348 )
349 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
350 assert e.value.details() == errors.INVALID_REGION
352 with pytest.raises(grpc.RpcError) as e:
353 api.UpdateProfile(
354 api_pb2.UpdateProfileReq(regions_lived=api_pb2.RepeatedStringValue(value=["United Kingdom"]))
355 )
356 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
357 assert e.value.details() == errors.INVALID_REGION
359 api.UpdateProfile(
360 api_pb2.UpdateProfileReq(
361 name=wrappers_pb2.StringValue(value="New name"),
362 city=wrappers_pb2.StringValue(value="Timbuktu"),
363 hometown=api_pb2.NullableStringValue(value="Walla Walla"),
364 lat=wrappers_pb2.DoubleValue(value=0.01),
365 lng=wrappers_pb2.DoubleValue(value=-2),
366 radius=wrappers_pb2.DoubleValue(value=321),
367 pronouns=api_pb2.NullableStringValue(value="Ro, Robo, Robots"),
368 occupation=api_pb2.NullableStringValue(value="Testing"),
369 education=api_pb2.NullableStringValue(value="Couchers U"),
370 about_me=api_pb2.NullableStringValue(value="I rule"),
371 my_travels=api_pb2.NullableStringValue(value="Oh the places you'll go!"),
372 things_i_like=api_pb2.NullableStringValue(value="Couchers"),
373 about_place=api_pb2.NullableStringValue(value="My place"),
374 hosting_status=api_pb2.HOSTING_STATUS_CAN_HOST,
375 meetup_status=api_pb2.MEETUP_STATUS_WANTS_TO_MEETUP,
376 language_abilities=api_pb2.RepeatedLanguageAbilityValue(
377 value=[
378 api_pb2.LanguageAbility(
379 code="eng",
380 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT,
381 )
382 ],
383 ),
384 regions_visited=api_pb2.RepeatedStringValue(value=["CXR", "FIN"]),
385 regions_lived=api_pb2.RepeatedStringValue(value=["USA", "EST"]),
386 additional_information=api_pb2.NullableStringValue(value="I <3 Couchers"),
387 )
388 )
390 user_details = api.GetUser(api_pb2.GetUserReq(user=user.username))
391 assert user_details.name == "New name"
392 assert user_details.city == "Timbuktu"
393 assert user_details.hometown == "Walla Walla"
394 assert user_details.pronouns == "Ro, Robo, Robots"
395 assert user_details.education == "Couchers U"
396 assert user_details.my_travels == "Oh the places you'll go!"
397 assert user_details.things_i_like == "Couchers"
398 assert user_details.lat == 0.01
399 assert user_details.lng == -2
400 assert user_details.radius == 321
401 assert user_details.occupation == "Testing"
402 assert user_details.about_me == "I rule"
403 assert user_details.about_place == "My place"
404 assert user_details.hosting_status == api_pb2.HOSTING_STATUS_CAN_HOST
405 assert user_details.meetup_status == api_pb2.MEETUP_STATUS_WANTS_TO_MEETUP
406 assert user_details.language_abilities[0].code == "eng"
407 assert user_details.language_abilities[0].fluency == api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT
408 assert user_details.additional_information == "I <3 Couchers"
409 assert user_details.regions_visited == ["CXR", "FIN"]
410 assert user_details.regions_lived == ["EST", "USA"]
412 # Test unset values
413 api.UpdateProfile(
414 api_pb2.UpdateProfileReq(
415 hometown=api_pb2.NullableStringValue(is_null=True),
416 radius=wrappers_pb2.DoubleValue(value=0),
417 pronouns=api_pb2.NullableStringValue(is_null=True),
418 occupation=api_pb2.NullableStringValue(is_null=True),
419 education=api_pb2.NullableStringValue(is_null=True),
420 about_me=api_pb2.NullableStringValue(is_null=True),
421 my_travels=api_pb2.NullableStringValue(is_null=True),
422 things_i_like=api_pb2.NullableStringValue(is_null=True),
423 about_place=api_pb2.NullableStringValue(is_null=True),
424 hosting_status=api_pb2.HOSTING_STATUS_CAN_HOST,
425 meetup_status=api_pb2.MEETUP_STATUS_WANTS_TO_MEETUP,
426 language_abilities=api_pb2.RepeatedLanguageAbilityValue(value=[]),
427 regions_visited=api_pb2.RepeatedStringValue(value=[]),
428 regions_lived=api_pb2.RepeatedStringValue(value=[]),
429 additional_information=api_pb2.NullableStringValue(is_null=True),
430 )
431 )
433 user_details = api.GetUser(api_pb2.GetUserReq(user=user.username))
434 assert not user_details.hometown
435 assert not user_details.radius
436 assert not user_details.pronouns
437 assert not user_details.occupation
438 assert not user_details.education
439 assert not user_details.about_me
440 assert not user_details.my_travels
441 assert not user_details.things_i_like
442 assert not user_details.about_place
443 assert user_details.hosting_status == api_pb2.HOSTING_STATUS_CAN_HOST
444 assert user_details.meetup_status == api_pb2.MEETUP_STATUS_WANTS_TO_MEETUP
445 assert not user_details.language_abilities
446 assert not user_details.regions_visited
447 assert not user_details.regions_lived
448 assert not user_details.additional_information
451def test_update_profile_do_not_email(db):
452 user, token = generate_user()
454 with notifications_session(token) as notifications:
455 notifications.SetNotificationSettings(notifications_pb2.SetNotificationSettingsReq(enable_do_not_email=True))
457 with api_session(token) as api:
458 with pytest.raises(grpc.RpcError) as e:
459 api.UpdateProfile(api_pb2.UpdateProfileReq(hosting_status=api_pb2.HOSTING_STATUS_CAN_HOST))
460 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
461 assert e.value.details() == errors.DO_NOT_EMAIL_CANNOT_HOST
463 with pytest.raises(grpc.RpcError) as e:
464 api.UpdateProfile(api_pb2.UpdateProfileReq(meetup_status=api_pb2.MEETUP_STATUS_OPEN_TO_MEETUP))
465 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
466 assert e.value.details() == errors.DO_NOT_EMAIL_CANNOT_MEET
469def test_language_abilities(db):
470 user, token = generate_user()
472 with api_session(token) as api:
473 res = api.GetUser(api_pb2.GetUserReq(user=user.username))
474 assert len(res.language_abilities) == 2
476 # can't add non-existent languages
477 with pytest.raises(grpc.RpcError) as e:
478 res = api.UpdateProfile(
479 api_pb2.UpdateProfileReq(
480 language_abilities=api_pb2.RepeatedLanguageAbilityValue(
481 value=[
482 api_pb2.LanguageAbility(
483 code="QQQ",
484 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT,
485 )
486 ],
487 ),
488 )
489 )
490 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
491 assert e.value.details() == errors.INVALID_LANGUAGE
493 # can't have multiple languages of the same type
494 with pytest.raises(Exception) as e:
495 res = api.UpdateProfile(
496 api_pb2.UpdateProfileReq(
497 language_abilities=api_pb2.RepeatedLanguageAbilityValue(
498 value=[
499 api_pb2.LanguageAbility(
500 code="eng",
501 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT,
502 ),
503 api_pb2.LanguageAbility(
504 code="eng",
505 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT,
506 ),
507 ],
508 ),
509 )
510 )
511 assert "violates unique constraint" in str(e.value)
513 # nothing changed
514 res = api.GetUser(api_pb2.GetUserReq(user=user.username))
515 assert len(res.language_abilities) == 2
517 # now actually add a value
518 api.UpdateProfile(
519 api_pb2.UpdateProfileReq(
520 language_abilities=api_pb2.RepeatedLanguageAbilityValue(
521 value=[
522 api_pb2.LanguageAbility(
523 code="eng",
524 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT,
525 )
526 ],
527 ),
528 )
529 )
531 res = api.GetUser(api_pb2.GetUserReq(user=user.username))
532 assert len(res.language_abilities) == 1
533 assert res.language_abilities[0].code == "eng"
534 assert res.language_abilities[0].fluency == api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT
536 # change the value to a new one
537 api.UpdateProfile(
538 api_pb2.UpdateProfileReq(
539 language_abilities=api_pb2.RepeatedLanguageAbilityValue(
540 value=[
541 api_pb2.LanguageAbility(
542 code="fin",
543 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_BEGINNER,
544 )
545 ],
546 ),
547 )
548 )
550 res = api.GetUser(api_pb2.GetUserReq(user=user.username))
551 assert len(res.language_abilities) == 1
552 assert res.language_abilities[0].code == "fin"
553 assert res.language_abilities[0].fluency == api_pb2.LanguageAbility.Fluency.FLUENCY_BEGINNER
555 # should be able to set to same value still
556 api.UpdateProfile(
557 api_pb2.UpdateProfileReq(
558 language_abilities=api_pb2.RepeatedLanguageAbilityValue(
559 value=[
560 api_pb2.LanguageAbility(
561 code="fin",
562 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_BEGINNER,
563 )
564 ],
565 ),
566 )
567 )
569 res = api.GetUser(api_pb2.GetUserReq(user=user.username))
570 assert len(res.language_abilities) == 1
571 assert res.language_abilities[0].code == "fin"
572 assert res.language_abilities[0].fluency == api_pb2.LanguageAbility.Fluency.FLUENCY_BEGINNER
574 # don't change it
575 api.UpdateProfile(api_pb2.UpdateProfileReq())
577 res = api.GetUser(api_pb2.GetUserReq(user=user.username))
578 assert len(res.language_abilities) == 1
579 assert res.language_abilities[0].code == "fin"
580 assert res.language_abilities[0].fluency == api_pb2.LanguageAbility.Fluency.FLUENCY_BEGINNER
582 # remove value
583 api.UpdateProfile(
584 api_pb2.UpdateProfileReq(
585 language_abilities=api_pb2.RepeatedLanguageAbilityValue(
586 value=[],
587 ),
588 )
589 )
591 res = api.GetUser(api_pb2.GetUserReq(user=user.username))
592 assert len(res.language_abilities) == 0
595def test_pending_friend_request_count(db):
596 user1, token1 = generate_user()
597 user2, token2 = generate_user()
598 user3, token3 = generate_user()
600 with api_session(token2) as api:
601 res = api.Ping(api_pb2.PingReq())
602 assert res.pending_friend_request_count == 0
604 with api_session(token1) as api:
605 res = api.Ping(api_pb2.PingReq())
606 assert res.pending_friend_request_count == 0
607 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
608 res = api.Ping(api_pb2.PingReq())
609 assert res.pending_friend_request_count == 0
611 with api_session(token2) as api:
612 res = api.Ping(api_pb2.PingReq())
613 assert res.pending_friend_request_count == 1
615 with api_session(token2) as api:
616 # check it's there
617 res = api.ListFriendRequests(empty_pb2.Empty())
618 assert len(res.sent) == 0
619 assert len(res.received) == 1
621 assert res.received[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING
622 assert res.received[0].user_id == user1.id
624 fr_id = res.received[0].friend_request_id
626 # accept it
627 api.RespondFriendRequest(api_pb2.RespondFriendRequestReq(friend_request_id=fr_id, accept=True))
629 res = api.Ping(api_pb2.PingReq())
630 assert res.pending_friend_request_count == 0
633def test_friend_request_flow(db, push_collector):
634 user1, token1 = generate_user(complete_profile=True)
635 user2, token2 = generate_user(complete_profile=True)
636 user3, token3 = generate_user()
638 # send friend request from user1 to user2
639 with mock_notification_email() as mock:
640 with api_session(token1) as api:
641 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
643 push_collector.assert_user_has_single_matching(
644 user2.id,
645 title=f"{user1.name} wants to be your friend",
646 body=f"You've received a friend request from {user1.name}",
647 )
649 mock.assert_called_once()
650 e = email_fields(mock)
651 assert e.recipient == user2.email
652 assert e.subject == f"[TEST] {user1.name} wants to be your friend on Couchers.org!"
653 assert user2.name in e.plain
654 assert user2.name in e.html
655 assert user1.name in e.plain
656 assert user1.name in e.html
657 assert "http://localhost:5001/img/thumbnail/" not in e.plain
658 assert "http://localhost:5001/img/thumbnail/" in e.html
659 assert "http://localhost:3000/connections/friends/" in e.plain
660 assert "http://localhost:3000/connections/friends/" in e.html
662 with session_scope() as session:
663 friend_request_id = (
664 session.execute(
665 select(FriendRelationship).where(
666 FriendRelationship.from_user_id == user1.id and FriendRelationship.to_user_id == user2.id
667 )
668 ).scalar_one_or_none()
669 ).id
671 with api_session(token1) as api:
672 # check it went through
673 res = api.ListFriendRequests(empty_pb2.Empty())
674 assert len(res.sent) == 1
675 assert len(res.received) == 0
677 assert res.sent[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING
678 assert res.sent[0].user_id == user2.id
679 assert res.sent[0].friend_request_id == friend_request_id
681 with api_session(token2) as api:
682 # check it's there
683 res = api.ListFriendRequests(empty_pb2.Empty())
684 assert len(res.sent) == 0
685 assert len(res.received) == 1
687 assert res.received[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING
688 assert res.received[0].user_id == user1.id
690 fr_id = res.received[0].friend_request_id
692 # accept it
693 with mock_notification_email() as mock:
694 api.RespondFriendRequest(api_pb2.RespondFriendRequestReq(friend_request_id=fr_id, accept=True))
696 # check it's gone
697 res = api.ListFriendRequests(empty_pb2.Empty())
698 assert len(res.sent) == 0
699 assert len(res.received) == 0
701 # check we're friends now
702 res = api.ListFriends(empty_pb2.Empty())
703 assert len(res.user_ids) == 1
704 assert res.user_ids[0] == user1.id
706 push_collector.assert_user_has_count(user2.id, 1)
707 push_collector.assert_user_push_matches_fields(
708 user1.id,
709 title=f"{user2.name} accepted your friend request!",
710 body=f"{user2.name} has accepted your friend request",
711 )
713 mock.assert_called_once()
714 e = email_fields(mock)
715 assert e.recipient == user1.email
716 assert e.subject == f"[TEST] {user2.name} accepted your friend request!"
717 assert user1.name in e.plain
718 assert user1.name in e.html
719 assert user2.name in e.plain
720 assert user2.name in e.html
721 assert "http://localhost:5001/img/thumbnail/" not in e.plain
722 assert "http://localhost:5001/img/thumbnail/" in e.html
723 assert f"http://localhost:3000/user/{user2.username}" in e.plain
724 assert f"http://localhost:3000/user/{user2.username}" in e.html
726 with api_session(token1) as api:
727 # check it's gone
728 res = api.ListFriendRequests(empty_pb2.Empty())
729 assert len(res.sent) == 0
730 assert len(res.received) == 0
732 # check we're friends now
733 res = api.ListFriends(empty_pb2.Empty())
734 assert len(res.user_ids) == 1
735 assert res.user_ids[0] == user2.id
738def test_cant_friend_request_twice(db):
739 user1, token1 = generate_user()
740 user2, token2 = generate_user()
742 # send friend request from user1 to user2
743 with api_session(token1) as api:
744 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
746 with pytest.raises(grpc.RpcError) as e:
747 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
748 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
749 assert e.value.details() == errors.FRIENDS_ALREADY_OR_PENDING
752def test_cant_friend_request_pending(db):
753 user1, token1 = generate_user()
754 user2, token2 = generate_user()
756 # send friend request from user1 to user2
757 with api_session(token1) as api:
758 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
760 with api_session(token2) as api:
761 with pytest.raises(grpc.RpcError) as e:
762 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user1.id))
763 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
764 assert e.value.details() == errors.FRIENDS_ALREADY_OR_PENDING
767def test_cant_friend_request_already_friends(db):
768 user1, token1 = generate_user()
769 user2, token2 = generate_user()
770 make_friends(user1, user2)
772 with api_session(token1) as api:
773 with pytest.raises(grpc.RpcError) as e:
774 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
775 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
776 assert e.value.details() == errors.FRIENDS_ALREADY_OR_PENDING
778 with api_session(token2) as api:
779 with pytest.raises(grpc.RpcError) as e:
780 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user1.id))
781 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION
782 assert e.value.details() == errors.FRIENDS_ALREADY_OR_PENDING
785def test_ListFriends(db):
786 user1, token1 = generate_user()
787 user2, token2 = generate_user()
788 user3, token3 = generate_user()
790 # send friend request from user1 to user2 and user3
791 with api_session(token1) as api:
792 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
793 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user3.id))
795 with api_session(token3) as api:
796 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
798 with api_session(token2) as api:
799 res = api.ListFriendRequests(empty_pb2.Empty())
800 assert len(res.received) == 2
802 # order is an implementation detail
803 user1_req = [req for req in res.received if req.user_id == user1.id][0]
804 user3_req = [req for req in res.received if req.user_id == user3.id][0]
806 assert user1_req.state == api_pb2.FriendRequest.FriendRequestStatus.PENDING
807 assert user1_req.user_id == user1.id
808 api.RespondFriendRequest(
809 api_pb2.RespondFriendRequestReq(friend_request_id=user1_req.friend_request_id, accept=True)
810 )
812 assert user3_req.state == api_pb2.FriendRequest.FriendRequestStatus.PENDING
813 assert user3_req.user_id == user3.id
814 api.RespondFriendRequest(
815 api_pb2.RespondFriendRequestReq(friend_request_id=user3_req.friend_request_id, accept=True)
816 )
818 # check we now have two friends
819 res = api.ListFriends(empty_pb2.Empty())
820 assert len(res.user_ids) == 2
821 assert user1.id in res.user_ids
822 assert user3.id in res.user_ids
824 with api_session(token3) as api:
825 res = api.ListFriends(empty_pb2.Empty())
826 assert len(res.user_ids) == 1
827 assert user2.id in res.user_ids
829 res = api.ListFriendRequests(empty_pb2.Empty())
830 assert len(res.received) == 1
831 assert res.received[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING
832 assert res.received[0].user_id == user1.id
833 fr_id = res.received[0].friend_request_id
834 api.RespondFriendRequest(api_pb2.RespondFriendRequestReq(friend_request_id=fr_id, accept=True))
836 res = api.ListFriends(empty_pb2.Empty())
837 assert len(res.user_ids) == 2
838 assert user1.id in res.user_ids
839 assert user2.id in res.user_ids
841 with api_session(token1) as api:
842 res = api.ListFriends(empty_pb2.Empty())
843 assert len(res.user_ids) == 2
844 assert user2.id in res.user_ids
845 assert user3.id in res.user_ids
848def test_ListMutualFriends(db):
849 user1, token1 = generate_user()
850 user2, token2 = generate_user()
851 user3, token3 = generate_user()
852 user4, token4 = generate_user()
853 user5, token5 = generate_user()
855 # arrange friends like this: 1<->2, 1<->3, 1<->4, 1<->5, 3<->2, 3<->4,
856 # so 1 and 2 should have mutual friend 3 only
857 make_friends(user1, user2)
858 make_friends(user1, user3)
859 make_friends(user1, user4)
860 make_friends(user1, user5)
861 make_friends(user3, user2)
862 make_friends(user3, user4)
864 with api_session(token1) as api:
865 mutual_friends = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user2.id)).mutual_friends
866 assert len(mutual_friends) == 1
867 assert mutual_friends[0].user_id == user3.id
869 # and other way around same
870 with api_session(token2) as api:
871 mutual_friends = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user1.id)).mutual_friends
872 assert len(mutual_friends) == 1
873 assert mutual_friends[0].user_id == user3.id
875 # Check pending request doesn't have effect
876 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user5.id))
878 mutual_friends = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user1.id)).mutual_friends
879 assert len(mutual_friends) == 1
880 assert mutual_friends[0].user_id == user3.id
882 # both ways
883 with api_session(token1) as api:
884 mutual_friends = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user2.id)).mutual_friends
885 assert len(mutual_friends) == 1
886 assert mutual_friends[0].user_id == user3.id
889def test_mutual_friends_self(db):
890 user1, token1 = generate_user()
891 user2, token2 = generate_user()
892 user3, token3 = generate_user()
893 user4, token4 = generate_user()
895 make_friends(user1, user2)
896 make_friends(user2, user3)
897 make_friends(user1, user4)
899 with api_session(token1) as api:
900 res = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user1.id))
901 assert len(res.mutual_friends) == 0
903 with api_session(token2) as api:
904 res = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user2.id))
905 assert len(res.mutual_friends) == 0
907 with api_session(token3) as api:
908 res = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user3.id))
909 assert len(res.mutual_friends) == 0
911 with api_session(token4) as api:
912 res = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user4.id))
913 assert len(res.mutual_friends) == 0
916def test_CancelFriendRequest(db):
917 user1, token1 = generate_user()
918 user2, token2 = generate_user()
920 with api_session(token1) as api:
921 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
923 res = api.ListFriendRequests(empty_pb2.Empty())
924 assert res.sent[0].user_id == user2.id
925 fr_id = res.sent[0].friend_request_id
927 api.CancelFriendRequest(api_pb2.CancelFriendRequestReq(friend_request_id=fr_id))
929 # check it's gone
930 res = api.ListFriendRequests(empty_pb2.Empty())
931 assert len(res.sent) == 0
932 assert len(res.received) == 0
934 # check not friends
935 res = api.ListFriends(empty_pb2.Empty())
936 assert len(res.user_ids) == 0
938 with api_session(token2) as api:
939 # check it's gone
940 res = api.ListFriendRequests(empty_pb2.Empty())
941 assert len(res.sent) == 0
942 assert len(res.received) == 0
944 # check we're not friends
945 res = api.ListFriends(empty_pb2.Empty())
946 assert len(res.user_ids) == 0
948 with api_session(token1) as api:
949 # check we can send another friend req
950 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
952 res = api.ListFriendRequests(empty_pb2.Empty())
953 assert res.sent[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING
954 assert res.sent[0].user_id == user2.id
957def test_accept_friend_request(db):
958 user1, token1 = generate_user()
959 user2, token2 = generate_user()
961 with session_scope() as session:
962 friend_request = FriendRelationship(from_user_id=user1.id, to_user_id=user2.id, status=FriendStatus.pending)
963 session.add(friend_request)
964 session.commit()
965 friend_request_id = friend_request.id
967 with api_session(token2) as api:
968 # check request pending
969 res = api.ListFriendRequests(empty_pb2.Empty())
970 assert len(res.received) == 1
971 assert res.received[0].user_id == user1.id
973 api.RespondFriendRequest(api_pb2.RespondFriendRequestReq(friend_request_id=friend_request_id, accept=True))
975 # check request is gone
976 res = api.ListFriendRequests(empty_pb2.Empty())
977 assert len(res.sent) == 0
978 assert len(res.received) == 0
980 # check now friends
981 res = api.ListFriends(empty_pb2.Empty())
982 assert len(res.user_ids) == 1
983 assert res.user_ids[0] == user1.id
985 with api_session(token1) as api:
986 # check request gone
987 res = api.ListFriendRequests(empty_pb2.Empty())
988 assert len(res.sent) == 0
989 assert len(res.received) == 0
991 # check now friends
992 res = api.ListFriends(empty_pb2.Empty())
993 assert len(res.user_ids) == 1
994 assert res.user_ids[0] == user2.id
997def test_reject_friend_request(db):
998 user1, token1 = generate_user()
999 user2, token2 = generate_user()
1001 with api_session(token1) as api:
1002 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
1004 res = api.ListFriendRequests(empty_pb2.Empty())
1005 assert res.sent[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING
1006 assert res.sent[0].user_id == user2.id
1008 with api_session(token2) as api:
1009 res = api.ListFriendRequests(empty_pb2.Empty())
1010 assert res.received[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING
1011 assert res.received[0].user_id == user1.id
1013 fr_id = res.received[0].friend_request_id
1015 # reject it
1016 api.RespondFriendRequest(api_pb2.RespondFriendRequestReq(friend_request_id=fr_id, accept=False))
1018 # check it's gone
1019 res = api.ListFriendRequests(empty_pb2.Empty())
1020 assert len(res.sent) == 0
1021 assert len(res.received) == 0
1023 # check not friends
1024 res = api.ListFriends(empty_pb2.Empty())
1025 assert len(res.user_ids) == 0
1027 with api_session(token1) as api:
1028 # check it's gone
1029 res = api.ListFriendRequests(empty_pb2.Empty())
1030 assert len(res.sent) == 0
1031 assert len(res.received) == 0
1033 # check we're not friends
1034 res = api.ListFriends(empty_pb2.Empty())
1035 assert len(res.user_ids) == 0
1037 # check we can send another friend req
1038 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id))
1040 res = api.ListFriendRequests(empty_pb2.Empty())
1041 assert res.sent[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING
1042 assert res.sent[0].user_id == user2.id
1045def test_hosting_preferences(db):
1046 user1, token1 = generate_user()
1047 user2, token2 = generate_user()
1049 with api_session(token1) as api:
1050 res = api.GetUser(api_pb2.GetUserReq(user=user1.username))
1051 assert not res.HasField("max_guests")
1052 assert not res.HasField("last_minute")
1053 assert not res.HasField("has_pets")
1054 assert not res.HasField("accepts_pets")
1055 assert not res.HasField("pet_details")
1056 assert not res.HasField("has_kids")
1057 assert not res.HasField("accepts_kids")
1058 assert not res.HasField("kid_details")
1059 assert not res.HasField("has_housemates")
1060 assert not res.HasField("housemate_details")
1061 assert not res.HasField("wheelchair_accessible")
1062 assert res.smoking_allowed == api_pb2.SMOKING_LOCATION_UNKNOWN
1063 assert not res.HasField("smokes_at_home")
1064 assert not res.HasField("drinking_allowed")
1065 assert not res.HasField("drinks_at_home")
1066 assert not res.HasField("other_host_info")
1067 assert res.sleeping_arrangement == api_pb2.SLEEPING_ARRANGEMENT_UNKNOWN
1068 assert not res.HasField("sleeping_details")
1069 assert not res.HasField("area")
1070 assert not res.HasField("house_rules")
1071 assert not res.HasField("parking")
1072 assert res.parking_details == api_pb2.PARKING_DETAILS_UNKNOWN
1073 assert not res.HasField("camping_ok")
1075 api.UpdateProfile(
1076 api_pb2.UpdateProfileReq(
1077 max_guests=api_pb2.NullableUInt32Value(value=3),
1078 last_minute=api_pb2.NullableBoolValue(value=True),
1079 has_pets=api_pb2.NullableBoolValue(value=False),
1080 accepts_pets=api_pb2.NullableBoolValue(value=True),
1081 pet_details=api_pb2.NullableStringValue(value="I love dogs"),
1082 has_kids=api_pb2.NullableBoolValue(value=False),
1083 accepts_kids=api_pb2.NullableBoolValue(value=True),
1084 kid_details=api_pb2.NullableStringValue(value="I hate kids"),
1085 has_housemates=api_pb2.NullableBoolValue(value=False),
1086 housemate_details=api_pb2.NullableStringValue(value="I have 7 housemates"),
1087 wheelchair_accessible=api_pb2.NullableBoolValue(value=True),
1088 smoking_allowed=api_pb2.SMOKING_LOCATION_WINDOW,
1089 area=api_pb2.NullableStringValue(value="area!"),
1090 smokes_at_home=api_pb2.NullableBoolValue(value=False),
1091 drinking_allowed=api_pb2.NullableBoolValue(value=True),
1092 drinks_at_home=api_pb2.NullableBoolValue(value=False),
1093 other_host_info=api_pb2.NullableStringValue(value="I'm pretty swell"),
1094 sleeping_arrangement=api_pb2.SLEEPING_ARRANGEMENT_COMMON,
1095 sleeping_details=api_pb2.NullableStringValue(value="Couch in living room"),
1096 house_rules=api_pb2.NullableStringValue(value="RULES!"),
1097 parking=api_pb2.NullableBoolValue(value=True),
1098 parking_details=api_pb2.PARKING_DETAILS_PAID_ONSITE,
1099 camping_ok=api_pb2.NullableBoolValue(value=False),
1100 )
1101 )
1103 # Use a second user to view the hosting preferences just to check
1104 # that it is public information.
1105 with api_session(token2) as api:
1106 res = api.GetUser(api_pb2.GetUserReq(user=user1.username))
1107 assert res.max_guests.value == 3
1108 assert res.last_minute.value
1109 assert not res.has_pets.value
1110 assert res.accepts_pets.value
1111 assert res.pet_details.value == "I love dogs"
1112 assert not res.has_kids.value
1113 assert res.accepts_kids.value
1114 assert res.kid_details.value == "I hate kids"
1115 assert not res.has_housemates.value
1116 assert res.housemate_details.value == "I have 7 housemates"
1117 assert res.wheelchair_accessible.value
1118 assert res.smoking_allowed == api_pb2.SMOKING_LOCATION_WINDOW
1119 assert not res.smokes_at_home.value
1120 assert res.drinking_allowed.value
1121 assert not res.drinks_at_home.value
1122 assert res.other_host_info.value == "I'm pretty swell"
1123 assert res.sleeping_arrangement == api_pb2.SLEEPING_ARRANGEMENT_COMMON
1124 assert res.sleeping_details.value == "Couch in living room"
1125 assert res.area.value == "area!"
1126 assert res.house_rules.value == "RULES!"
1127 assert res.parking.value
1128 assert res.parking_details == api_pb2.PARKING_DETAILS_PAID_ONSITE
1129 assert not res.camping_ok.value
1131 # test unsetting
1132 with api_session(token1) as api:
1133 api.UpdateProfile(
1134 api_pb2.UpdateProfileReq(
1135 max_guests=api_pb2.NullableUInt32Value(is_null=True),
1136 last_minute=api_pb2.NullableBoolValue(is_null=True),
1137 has_pets=api_pb2.NullableBoolValue(is_null=True),
1138 accepts_pets=api_pb2.NullableBoolValue(is_null=True),
1139 pet_details=api_pb2.NullableStringValue(is_null=True),
1140 has_kids=api_pb2.NullableBoolValue(is_null=True),
1141 accepts_kids=api_pb2.NullableBoolValue(is_null=True),
1142 kid_details=api_pb2.NullableStringValue(is_null=True),
1143 has_housemates=api_pb2.NullableBoolValue(is_null=True),
1144 housemate_details=api_pb2.NullableStringValue(is_null=True),
1145 wheelchair_accessible=api_pb2.NullableBoolValue(is_null=True),
1146 smoking_allowed=api_pb2.SMOKING_LOCATION_UNKNOWN,
1147 area=api_pb2.NullableStringValue(is_null=True),
1148 smokes_at_home=api_pb2.NullableBoolValue(is_null=True),
1149 drinking_allowed=api_pb2.NullableBoolValue(is_null=True),
1150 drinks_at_home=api_pb2.NullableBoolValue(is_null=True),
1151 other_host_info=api_pb2.NullableStringValue(is_null=True),
1152 sleeping_arrangement=api_pb2.SLEEPING_ARRANGEMENT_UNKNOWN,
1153 sleeping_details=api_pb2.NullableStringValue(is_null=True),
1154 house_rules=api_pb2.NullableStringValue(is_null=True),
1155 parking=api_pb2.NullableBoolValue(is_null=True),
1156 parking_details=api_pb2.PARKING_DETAILS_UNKNOWN,
1157 camping_ok=api_pb2.NullableBoolValue(is_null=True),
1158 )
1159 )
1161 res = api.GetUser(api_pb2.GetUserReq(user=user1.username))
1162 assert not res.HasField("max_guests")
1163 assert not res.HasField("last_minute")
1164 assert not res.HasField("has_pets")
1165 assert not res.HasField("accepts_pets")
1166 assert not res.HasField("pet_details")
1167 assert not res.HasField("has_kids")
1168 assert not res.HasField("accepts_kids")
1169 assert not res.HasField("kid_details")
1170 assert not res.HasField("has_housemates")
1171 assert not res.HasField("housemate_details")
1172 assert not res.HasField("wheelchair_accessible")
1173 assert res.smoking_allowed == api_pb2.SMOKING_LOCATION_UNKNOWN
1174 assert not res.HasField("smokes_at_home")
1175 assert not res.HasField("drinking_allowed")
1176 assert not res.HasField("drinks_at_home")
1177 assert not res.HasField("other_host_info")
1178 assert res.sleeping_arrangement == api_pb2.SLEEPING_ARRANGEMENT_UNKNOWN
1179 assert not res.HasField("sleeping_details")
1180 assert not res.HasField("area")
1181 assert not res.HasField("house_rules")
1182 assert not res.HasField("parking")
1183 assert res.parking_details == api_pb2.PARKING_DETAILS_UNKNOWN
1184 assert not res.HasField("camping_ok")
1187def test_badges(db):
1188 user1, _ = generate_user()
1189 user2, _ = generate_user()
1190 user3, _ = generate_user()
1191 user4, token = generate_user()
1193 update_badges(empty_pb2.Empty())
1195 with api_session(token) as api:
1196 assert api.GetUser(api_pb2.GetUserReq(user=user1.username)).badges == ["founder", "board_member"]
1197 assert api.GetUser(api_pb2.GetUserReq(user=user2.username)).badges == ["founder", "board_member"]
1198 assert api.GetUser(api_pb2.GetUserReq(user=user3.username)).badges == []