Coverage for src/tests/test_api.py: 100%

600 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-07-20 21:46 +0000

1from datetime import timedelta 

2 

3import grpc 

4import pytest 

5from google.protobuf import empty_pb2, wrappers_pb2 

6 

7from couchers import errors 

8from couchers.db import session_scope 

9from couchers.jobs.handlers import update_badges 

10from couchers.models import FriendRelationship, FriendStatus 

11from couchers.sql import couchers_select as select 

12from couchers.utils import create_coordinate, to_aware_datetime 

13from proto import api_pb2, jail_pb2, notifications_pb2 

14from tests.test_fixtures import ( # noqa 

15 api_session, 

16 blocking_session, 

17 db, 

18 email_fields, 

19 generate_user, 

20 make_friends, 

21 make_user_block, 

22 make_user_invisible, 

23 mock_notification_email, 

24 notifications_session, 

25 push_collector, 

26 real_api_session, 

27 real_jail_session, 

28 testconfig, 

29) 

30 

31 

32@pytest.fixture(autouse=True) 

33def _(testconfig): 

34 pass 

35 

36 

37def test_ping(db): 

38 user, token = generate_user() 

39 

40 with real_api_session(token) as api: 

41 res = api.Ping(api_pb2.PingReq()) 

42 

43 assert res.user.user_id == user.id 

44 assert res.user.username == user.username 

45 assert res.user.name == user.name 

46 assert res.user.city == user.city 

47 assert res.user.hometown == user.hometown 

48 assert res.user.verification == 0.0 

49 assert res.user.community_standing == user.community_standing 

50 assert res.user.num_references == 0 

51 assert res.user.gender == user.gender 

52 assert res.user.pronouns == user.pronouns 

53 assert res.user.age == user.age 

54 

55 assert (res.user.lat, res.user.lng) == (user.coordinates or (0, 0)) 

56 

57 # the joined time is fuzzed 

58 # but shouldn't be before actual joined time, or more than one hour behind 

59 assert user.joined - timedelta(hours=1) <= to_aware_datetime(res.user.joined) <= user.joined 

60 # same for last_active 

61 assert user.last_active - timedelta(hours=1) <= to_aware_datetime(res.user.last_active) <= user.last_active 

62 

63 assert res.user.hosting_status == api_pb2.HOSTING_STATUS_CANT_HOST 

64 assert res.user.meetup_status == api_pb2.MEETUP_STATUS_OPEN_TO_MEETUP 

65 

66 assert res.user.occupation == user.occupation 

67 assert res.user.education == user.education 

68 assert res.user.about_me == user.about_me 

69 assert res.user.my_travels == user.my_travels 

70 assert res.user.things_i_like == user.things_i_like 

71 assert {language_ability.code for language_ability in res.user.language_abilities} == {"fin", "fra"} 

72 assert res.user.about_place == user.about_place 

73 assert res.user.regions_visited == ["FIN", "REU", "CHE"] # Tests alphabetization by region name 

74 assert res.user.regions_lived == ["EST", "FRA", "ESP"] # Ditto 

75 assert res.user.additional_information == user.additional_information 

76 

77 assert res.user.friends == api_pb2.User.FriendshipStatus.NA 

78 assert not res.user.HasField("pending_friend_request") 

79 

80 

81def test_coords(db): 

82 # make them have not added a location 

83 user1, token1 = generate_user(geom=None, geom_radius=None) 

84 user2, token2 = generate_user() 

85 

86 with api_session(token2) as api: 

87 res = api.Ping(api_pb2.PingReq()) 

88 assert res.user.city == user2.city 

89 lat, lng = user2.coordinates or (0, 0) 

90 assert res.user.lat == lat 

91 assert res.user.lng == lng 

92 assert res.user.radius == user2.geom_radius 

93 

94 with api_session(token2) as api: 

95 res = api.GetUser(api_pb2.GetUserReq(user=user1.username)) 

96 assert res.city == user1.city 

97 assert res.lat == 0.0 

98 assert res.lng == 0.0 

99 assert res.radius == 0.0 

100 

101 # Check coordinate wrapping 

102 user3, token3 = generate_user(geom=create_coordinate(40.0, -180.5)) 

103 user4, token4 = generate_user(geom=create_coordinate(40.0, 20.0)) 

104 user5, token5 = generate_user(geom=create_coordinate(90.5, 20.0)) 

105 

106 with api_session(token3) as api: 

107 res = api.GetUser(api_pb2.GetUserReq(user=user3.username)) 

108 assert res.lat == 40.0 

109 assert res.lng == 179.5 

110 

111 with api_session(token4) as api: 

112 res = api.GetUser(api_pb2.GetUserReq(user=user4.username)) 

113 assert res.lat == 40.0 

114 assert res.lng == 20.0 

115 

116 # PostGIS does not wrap longitude for latitude overflow 

117 with api_session(token5) as api: 

118 res = api.GetUser(api_pb2.GetUserReq(user=user5.username)) 

119 assert res.lat == 89.5 

120 assert res.lng == 20.0 

121 

122 with real_jail_session(token1) as jail: 

123 res = jail.JailInfo(empty_pb2.Empty()) 

124 assert res.jailed 

125 assert res.has_not_added_location 

126 

127 res = jail.SetLocation( 

128 jail_pb2.SetLocationReq( 

129 city="New York City", 

130 lat=40.7812, 

131 lng=-73.9647, 

132 radius=250, 

133 ) 

134 ) 

135 

136 assert not res.jailed 

137 assert not res.has_not_added_location 

138 

139 res = jail.JailInfo(empty_pb2.Empty()) 

140 assert not res.jailed 

141 assert not res.has_not_added_location 

142 

143 with api_session(token2) as api: 

144 res = api.GetUser(api_pb2.GetUserReq(user=user1.username)) 

145 assert res.city == "New York City" 

146 assert res.lat == 40.7812 

147 assert res.lng == -73.9647 

148 assert res.radius == 250 

149 

150 

151def test_get_user(db): 

152 user1, token1 = generate_user() 

153 user2, token2 = generate_user() 

154 

155 with api_session(token1) as api: 

156 res = api.GetUser(api_pb2.GetUserReq(user=user2.username)) 

157 assert res.user_id == user2.id 

158 assert res.username == user2.username 

159 assert res.name == user2.name 

160 

161 with api_session(token1) as api: 

162 res = api.GetUser(api_pb2.GetUserReq(user=str(user2.id))) 

163 assert res.user_id == user2.id 

164 assert res.username == user2.username 

165 assert res.name == user2.name 

166 

167 

168def test_update_profile(db): 

169 user, token = generate_user() 

170 

171 with api_session(token) as api: 

172 with pytest.raises(grpc.RpcError) as e: 

173 api.UpdateProfile(api_pb2.UpdateProfileReq(name=wrappers_pb2.StringValue(value=" "))) 

174 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

175 assert e.value.details() == errors.INVALID_NAME 

176 

177 with pytest.raises(grpc.RpcError) as e: 

178 api.UpdateProfile( 

179 api_pb2.UpdateProfileReq(lat=wrappers_pb2.DoubleValue(value=0), lng=wrappers_pb2.DoubleValue(value=0)) 

180 ) 

181 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

182 assert e.value.details() == errors.INVALID_COORDINATE 

183 

184 with pytest.raises(grpc.RpcError) as e: 

185 api.UpdateProfile( 

186 api_pb2.UpdateProfileReq(regions_visited=api_pb2.RepeatedStringValue(value=["United States"])) 

187 ) 

188 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

189 assert e.value.details() == errors.INVALID_REGION 

190 

191 with pytest.raises(grpc.RpcError) as e: 

192 api.UpdateProfile( 

193 api_pb2.UpdateProfileReq(regions_lived=api_pb2.RepeatedStringValue(value=["United Kingdom"])) 

194 ) 

195 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

196 assert e.value.details() == errors.INVALID_REGION 

197 

198 api.UpdateProfile( 

199 api_pb2.UpdateProfileReq( 

200 name=wrappers_pb2.StringValue(value="New name"), 

201 city=wrappers_pb2.StringValue(value="Timbuktu"), 

202 hometown=api_pb2.NullableStringValue(value="Walla Walla"), 

203 lat=wrappers_pb2.DoubleValue(value=0.01), 

204 lng=wrappers_pb2.DoubleValue(value=-2), 

205 radius=wrappers_pb2.DoubleValue(value=321), 

206 pronouns=api_pb2.NullableStringValue(value="Ro, Robo, Robots"), 

207 occupation=api_pb2.NullableStringValue(value="Testing"), 

208 education=api_pb2.NullableStringValue(value="Couchers U"), 

209 about_me=api_pb2.NullableStringValue(value="I rule"), 

210 my_travels=api_pb2.NullableStringValue(value="Oh the places you'll go!"), 

211 things_i_like=api_pb2.NullableStringValue(value="Couchers"), 

212 about_place=api_pb2.NullableStringValue(value="My place"), 

213 hosting_status=api_pb2.HOSTING_STATUS_CAN_HOST, 

214 meetup_status=api_pb2.MEETUP_STATUS_WANTS_TO_MEETUP, 

215 language_abilities=api_pb2.RepeatedLanguageAbilityValue( 

216 value=[ 

217 api_pb2.LanguageAbility( 

218 code="eng", 

219 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT, 

220 ) 

221 ], 

222 ), 

223 regions_visited=api_pb2.RepeatedStringValue(value=["CXR", "FIN"]), 

224 regions_lived=api_pb2.RepeatedStringValue(value=["USA", "EST"]), 

225 additional_information=api_pb2.NullableStringValue(value="I <3 Couchers"), 

226 ) 

227 ) 

228 

229 user_details = api.GetUser(api_pb2.GetUserReq(user=user.username)) 

230 assert user_details.name == "New name" 

231 assert user_details.city == "Timbuktu" 

232 assert user_details.hometown == "Walla Walla" 

233 assert user_details.pronouns == "Ro, Robo, Robots" 

234 assert user_details.education == "Couchers U" 

235 assert user_details.my_travels == "Oh the places you'll go!" 

236 assert user_details.things_i_like == "Couchers" 

237 assert user_details.lat == 0.01 

238 assert user_details.lng == -2 

239 assert user_details.radius == 321 

240 assert user_details.occupation == "Testing" 

241 assert user_details.about_me == "I rule" 

242 assert user_details.about_place == "My place" 

243 assert user_details.hosting_status == api_pb2.HOSTING_STATUS_CAN_HOST 

244 assert user_details.meetup_status == api_pb2.MEETUP_STATUS_WANTS_TO_MEETUP 

245 assert user_details.language_abilities[0].code == "eng" 

246 assert user_details.language_abilities[0].fluency == api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT 

247 assert user_details.additional_information == "I <3 Couchers" 

248 assert user_details.regions_visited == ["CXR", "FIN"] 

249 assert user_details.regions_lived == ["EST", "USA"] 

250 

251 # Test unset values 

252 api.UpdateProfile( 

253 api_pb2.UpdateProfileReq( 

254 hometown=api_pb2.NullableStringValue(is_null=True), 

255 radius=wrappers_pb2.DoubleValue(value=0), 

256 pronouns=api_pb2.NullableStringValue(is_null=True), 

257 occupation=api_pb2.NullableStringValue(is_null=True), 

258 education=api_pb2.NullableStringValue(is_null=True), 

259 about_me=api_pb2.NullableStringValue(is_null=True), 

260 my_travels=api_pb2.NullableStringValue(is_null=True), 

261 things_i_like=api_pb2.NullableStringValue(is_null=True), 

262 about_place=api_pb2.NullableStringValue(is_null=True), 

263 hosting_status=api_pb2.HOSTING_STATUS_CAN_HOST, 

264 meetup_status=api_pb2.MEETUP_STATUS_WANTS_TO_MEETUP, 

265 language_abilities=api_pb2.RepeatedLanguageAbilityValue(value=[]), 

266 regions_visited=api_pb2.RepeatedStringValue(value=[]), 

267 regions_lived=api_pb2.RepeatedStringValue(value=[]), 

268 additional_information=api_pb2.NullableStringValue(is_null=True), 

269 ) 

270 ) 

271 

272 user_details = api.GetUser(api_pb2.GetUserReq(user=user.username)) 

273 assert not user_details.hometown 

274 assert not user_details.radius 

275 assert not user_details.pronouns 

276 assert not user_details.occupation 

277 assert not user_details.education 

278 assert not user_details.about_me 

279 assert not user_details.my_travels 

280 assert not user_details.things_i_like 

281 assert not user_details.about_place 

282 assert user_details.hosting_status == api_pb2.HOSTING_STATUS_CAN_HOST 

283 assert user_details.meetup_status == api_pb2.MEETUP_STATUS_WANTS_TO_MEETUP 

284 assert not user_details.language_abilities 

285 assert not user_details.regions_visited 

286 assert not user_details.regions_lived 

287 assert not user_details.additional_information 

288 

289 

290def test_update_profile_do_not_email(db): 

291 user, token = generate_user() 

292 

293 with notifications_session(token) as notifications: 

294 notifications.SetNotificationSettings(notifications_pb2.SetNotificationSettingsReq(enable_do_not_email=True)) 

295 

296 with api_session(token) as api: 

297 with pytest.raises(grpc.RpcError) as e: 

298 api.UpdateProfile(api_pb2.UpdateProfileReq(hosting_status=api_pb2.HOSTING_STATUS_CAN_HOST)) 

299 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

300 assert e.value.details() == errors.DO_NOT_EMAIL_CANNOT_HOST 

301 

302 with pytest.raises(grpc.RpcError) as e: 

303 api.UpdateProfile(api_pb2.UpdateProfileReq(meetup_status=api_pb2.MEETUP_STATUS_OPEN_TO_MEETUP)) 

304 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

305 assert e.value.details() == errors.DO_NOT_EMAIL_CANNOT_MEET 

306 

307 

308def test_language_abilities(db): 

309 user, token = generate_user() 

310 

311 with api_session(token) as api: 

312 res = api.GetUser(api_pb2.GetUserReq(user=user.username)) 

313 assert len(res.language_abilities) == 2 

314 

315 # can't add non-existent languages 

316 with pytest.raises(grpc.RpcError) as e: 

317 res = api.UpdateProfile( 

318 api_pb2.UpdateProfileReq( 

319 language_abilities=api_pb2.RepeatedLanguageAbilityValue( 

320 value=[ 

321 api_pb2.LanguageAbility( 

322 code="QQQ", 

323 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT, 

324 ) 

325 ], 

326 ), 

327 ) 

328 ) 

329 assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

330 assert e.value.details() == errors.INVALID_LANGUAGE 

331 

332 # can't have multiple languages of the same type 

333 with pytest.raises(Exception) as e: 

334 res = api.UpdateProfile( 

335 api_pb2.UpdateProfileReq( 

336 language_abilities=api_pb2.RepeatedLanguageAbilityValue( 

337 value=[ 

338 api_pb2.LanguageAbility( 

339 code="eng", 

340 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT, 

341 ), 

342 api_pb2.LanguageAbility( 

343 code="eng", 

344 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT, 

345 ), 

346 ], 

347 ), 

348 ) 

349 ) 

350 assert "violates unique constraint" in str(e.value) 

351 

352 # nothing changed 

353 res = api.GetUser(api_pb2.GetUserReq(user=user.username)) 

354 assert len(res.language_abilities) == 2 

355 

356 # now actually add a value 

357 api.UpdateProfile( 

358 api_pb2.UpdateProfileReq( 

359 language_abilities=api_pb2.RepeatedLanguageAbilityValue( 

360 value=[ 

361 api_pb2.LanguageAbility( 

362 code="eng", 

363 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT, 

364 ) 

365 ], 

366 ), 

367 ) 

368 ) 

369 

370 res = api.GetUser(api_pb2.GetUserReq(user=user.username)) 

371 assert len(res.language_abilities) == 1 

372 assert res.language_abilities[0].code == "eng" 

373 assert res.language_abilities[0].fluency == api_pb2.LanguageAbility.Fluency.FLUENCY_FLUENT 

374 

375 # change the value to a new one 

376 api.UpdateProfile( 

377 api_pb2.UpdateProfileReq( 

378 language_abilities=api_pb2.RepeatedLanguageAbilityValue( 

379 value=[ 

380 api_pb2.LanguageAbility( 

381 code="fin", 

382 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_BEGINNER, 

383 ) 

384 ], 

385 ), 

386 ) 

387 ) 

388 

389 res = api.GetUser(api_pb2.GetUserReq(user=user.username)) 

390 assert len(res.language_abilities) == 1 

391 assert res.language_abilities[0].code == "fin" 

392 assert res.language_abilities[0].fluency == api_pb2.LanguageAbility.Fluency.FLUENCY_BEGINNER 

393 

394 # should be able to set to same value still 

395 api.UpdateProfile( 

396 api_pb2.UpdateProfileReq( 

397 language_abilities=api_pb2.RepeatedLanguageAbilityValue( 

398 value=[ 

399 api_pb2.LanguageAbility( 

400 code="fin", 

401 fluency=api_pb2.LanguageAbility.Fluency.FLUENCY_BEGINNER, 

402 ) 

403 ], 

404 ), 

405 ) 

406 ) 

407 

408 res = api.GetUser(api_pb2.GetUserReq(user=user.username)) 

409 assert len(res.language_abilities) == 1 

410 assert res.language_abilities[0].code == "fin" 

411 assert res.language_abilities[0].fluency == api_pb2.LanguageAbility.Fluency.FLUENCY_BEGINNER 

412 

413 # don't change it 

414 api.UpdateProfile(api_pb2.UpdateProfileReq()) 

415 

416 res = api.GetUser(api_pb2.GetUserReq(user=user.username)) 

417 assert len(res.language_abilities) == 1 

418 assert res.language_abilities[0].code == "fin" 

419 assert res.language_abilities[0].fluency == api_pb2.LanguageAbility.Fluency.FLUENCY_BEGINNER 

420 

421 # remove value 

422 api.UpdateProfile( 

423 api_pb2.UpdateProfileReq( 

424 language_abilities=api_pb2.RepeatedLanguageAbilityValue( 

425 value=[], 

426 ), 

427 ) 

428 ) 

429 

430 res = api.GetUser(api_pb2.GetUserReq(user=user.username)) 

431 assert len(res.language_abilities) == 0 

432 

433 

434def test_pending_friend_request_count(db): 

435 user1, token1 = generate_user() 

436 user2, token2 = generate_user() 

437 user3, token3 = generate_user() 

438 

439 with api_session(token2) as api: 

440 res = api.Ping(api_pb2.PingReq()) 

441 assert res.pending_friend_request_count == 0 

442 

443 with api_session(token1) as api: 

444 res = api.Ping(api_pb2.PingReq()) 

445 assert res.pending_friend_request_count == 0 

446 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

447 res = api.Ping(api_pb2.PingReq()) 

448 assert res.pending_friend_request_count == 0 

449 

450 with api_session(token2) as api: 

451 res = api.Ping(api_pb2.PingReq()) 

452 assert res.pending_friend_request_count == 1 

453 

454 with api_session(token2) as api: 

455 # check it's there 

456 res = api.ListFriendRequests(empty_pb2.Empty()) 

457 assert len(res.sent) == 0 

458 assert len(res.received) == 1 

459 

460 assert res.received[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING 

461 assert res.received[0].user_id == user1.id 

462 

463 fr_id = res.received[0].friend_request_id 

464 

465 # accept it 

466 api.RespondFriendRequest(api_pb2.RespondFriendRequestReq(friend_request_id=fr_id, accept=True)) 

467 

468 res = api.Ping(api_pb2.PingReq()) 

469 assert res.pending_friend_request_count == 0 

470 

471 

472def test_friend_request_flow(db, push_collector): 

473 user1, token1 = generate_user(complete_profile=True) 

474 user2, token2 = generate_user(complete_profile=True) 

475 user3, token3 = generate_user() 

476 

477 # send friend request from user1 to user2 

478 with mock_notification_email() as mock: 

479 with api_session(token1) as api: 

480 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

481 

482 push_collector.assert_user_has_single_matching( 

483 user2.id, 

484 title=f"{user1.name} wants to be your friend", 

485 body=f"You've received a friend request from {user1.name}", 

486 ) 

487 

488 mock.assert_called_once() 

489 e = email_fields(mock) 

490 assert e.recipient == user2.email 

491 assert e.subject == f"[TEST] {user1.name} wants to be your friend on Couchers.org!" 

492 assert user2.name in e.plain 

493 assert user2.name in e.html 

494 assert user1.name in e.plain 

495 assert user1.name in e.html 

496 assert "http://localhost:5000/img/thumbnail/" not in e.plain 

497 assert "http://localhost:5000/img/thumbnail/" in e.html 

498 assert "http://localhost:3000/connections/friends/" in e.plain 

499 assert "http://localhost:3000/connections/friends/" in e.html 

500 

501 with session_scope() as session: 

502 friend_request_id = ( 

503 session.execute( 

504 select(FriendRelationship).where( 

505 FriendRelationship.from_user_id == user1.id and FriendRelationship.to_user_id == user2.id 

506 ) 

507 ).scalar_one_or_none() 

508 ).id 

509 

510 with api_session(token1) as api: 

511 # check it went through 

512 res = api.ListFriendRequests(empty_pb2.Empty()) 

513 assert len(res.sent) == 1 

514 assert len(res.received) == 0 

515 

516 assert res.sent[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING 

517 assert res.sent[0].user_id == user2.id 

518 assert res.sent[0].friend_request_id == friend_request_id 

519 

520 with api_session(token2) as api: 

521 # check it's there 

522 res = api.ListFriendRequests(empty_pb2.Empty()) 

523 assert len(res.sent) == 0 

524 assert len(res.received) == 1 

525 

526 assert res.received[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING 

527 assert res.received[0].user_id == user1.id 

528 

529 fr_id = res.received[0].friend_request_id 

530 

531 # accept it 

532 with mock_notification_email() as mock: 

533 api.RespondFriendRequest(api_pb2.RespondFriendRequestReq(friend_request_id=fr_id, accept=True)) 

534 

535 # check it's gone 

536 res = api.ListFriendRequests(empty_pb2.Empty()) 

537 assert len(res.sent) == 0 

538 assert len(res.received) == 0 

539 

540 # check we're friends now 

541 res = api.ListFriends(empty_pb2.Empty()) 

542 assert len(res.user_ids) == 1 

543 assert res.user_ids[0] == user1.id 

544 

545 push_collector.assert_user_has_count(user2.id, 1) 

546 push_collector.assert_user_push_matches_fields( 

547 user1.id, 

548 title=f"{user2.name} accepted your friend request!", 

549 body=f"{user2.name} has accepted your friend request", 

550 ) 

551 

552 mock.assert_called_once() 

553 e = email_fields(mock) 

554 assert e.recipient == user1.email 

555 assert e.subject == f"[TEST] {user2.name} accepted your friend request!" 

556 assert user1.name in e.plain 

557 assert user1.name in e.html 

558 assert user2.name in e.plain 

559 assert user2.name in e.html 

560 assert "http://localhost:5000/img/thumbnail/" not in e.plain 

561 assert "http://localhost:5000/img/thumbnail/" in e.html 

562 assert f"http://localhost:3000/user/{user2.username}" in e.plain 

563 assert f"http://localhost:3000/user/{user2.username}" in e.html 

564 

565 with api_session(token1) as api: 

566 # check it's gone 

567 res = api.ListFriendRequests(empty_pb2.Empty()) 

568 assert len(res.sent) == 0 

569 assert len(res.received) == 0 

570 

571 # check we're friends now 

572 res = api.ListFriends(empty_pb2.Empty()) 

573 assert len(res.user_ids) == 1 

574 assert res.user_ids[0] == user2.id 

575 

576 

577def test_cant_friend_request_twice(db): 

578 user1, token1 = generate_user() 

579 user2, token2 = generate_user() 

580 

581 # send friend request from user1 to user2 

582 with api_session(token1) as api: 

583 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

584 

585 with pytest.raises(grpc.RpcError) as e: 

586 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

587 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

588 assert e.value.details() == errors.FRIENDS_ALREADY_OR_PENDING 

589 

590 

591def test_cant_friend_request_pending(db): 

592 user1, token1 = generate_user() 

593 user2, token2 = generate_user() 

594 

595 # send friend request from user1 to user2 

596 with api_session(token1) as api: 

597 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

598 

599 with api_session(token2) as api: 

600 with pytest.raises(grpc.RpcError) as e: 

601 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user1.id)) 

602 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

603 assert e.value.details() == errors.FRIENDS_ALREADY_OR_PENDING 

604 

605 

606def test_cant_friend_request_already_friends(db): 

607 user1, token1 = generate_user() 

608 user2, token2 = generate_user() 

609 make_friends(user1, user2) 

610 

611 with api_session(token1) as api: 

612 with pytest.raises(grpc.RpcError) as e: 

613 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

614 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

615 assert e.value.details() == errors.FRIENDS_ALREADY_OR_PENDING 

616 

617 with api_session(token2) as api: 

618 with pytest.raises(grpc.RpcError) as e: 

619 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user1.id)) 

620 assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

621 assert e.value.details() == errors.FRIENDS_ALREADY_OR_PENDING 

622 

623 

624def test_ListFriends(db): 

625 user1, token1 = generate_user() 

626 user2, token2 = generate_user() 

627 user3, token3 = generate_user() 

628 

629 # send friend request from user1 to user2 and user3 

630 with api_session(token1) as api: 

631 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

632 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user3.id)) 

633 

634 with api_session(token3) as api: 

635 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

636 

637 with api_session(token2) as api: 

638 res = api.ListFriendRequests(empty_pb2.Empty()) 

639 assert len(res.received) == 2 

640 

641 # order is an implementation detail 

642 user1_req = [req for req in res.received if req.user_id == user1.id][0] 

643 user3_req = [req for req in res.received if req.user_id == user3.id][0] 

644 

645 assert user1_req.state == api_pb2.FriendRequest.FriendRequestStatus.PENDING 

646 assert user1_req.user_id == user1.id 

647 api.RespondFriendRequest( 

648 api_pb2.RespondFriendRequestReq(friend_request_id=user1_req.friend_request_id, accept=True) 

649 ) 

650 

651 assert user3_req.state == api_pb2.FriendRequest.FriendRequestStatus.PENDING 

652 assert user3_req.user_id == user3.id 

653 api.RespondFriendRequest( 

654 api_pb2.RespondFriendRequestReq(friend_request_id=user3_req.friend_request_id, accept=True) 

655 ) 

656 

657 # check we now have two friends 

658 res = api.ListFriends(empty_pb2.Empty()) 

659 assert len(res.user_ids) == 2 

660 assert user1.id in res.user_ids 

661 assert user3.id in res.user_ids 

662 

663 with api_session(token3) as api: 

664 res = api.ListFriends(empty_pb2.Empty()) 

665 assert len(res.user_ids) == 1 

666 assert user2.id in res.user_ids 

667 

668 res = api.ListFriendRequests(empty_pb2.Empty()) 

669 assert len(res.received) == 1 

670 assert res.received[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING 

671 assert res.received[0].user_id == user1.id 

672 fr_id = res.received[0].friend_request_id 

673 api.RespondFriendRequest(api_pb2.RespondFriendRequestReq(friend_request_id=fr_id, accept=True)) 

674 

675 res = api.ListFriends(empty_pb2.Empty()) 

676 assert len(res.user_ids) == 2 

677 assert user1.id in res.user_ids 

678 assert user2.id in res.user_ids 

679 

680 with api_session(token1) as api: 

681 res = api.ListFriends(empty_pb2.Empty()) 

682 assert len(res.user_ids) == 2 

683 assert user2.id in res.user_ids 

684 assert user3.id in res.user_ids 

685 

686 

687def test_ListMutualFriends(db): 

688 user1, token1 = generate_user() 

689 user2, token2 = generate_user() 

690 user3, token3 = generate_user() 

691 user4, token4 = generate_user() 

692 user5, token5 = generate_user() 

693 

694 # arrange friends like this: 1<->2, 1<->3, 1<->4, 1<->5, 3<->2, 3<->4, 

695 # so 1 and 2 should have mutual friend 3 only 

696 make_friends(user1, user2) 

697 make_friends(user1, user3) 

698 make_friends(user1, user4) 

699 make_friends(user1, user5) 

700 make_friends(user3, user2) 

701 make_friends(user3, user4) 

702 

703 with api_session(token1) as api: 

704 mutual_friends = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user2.id)).mutual_friends 

705 assert len(mutual_friends) == 1 

706 assert mutual_friends[0].user_id == user3.id 

707 

708 # and other way around same 

709 with api_session(token2) as api: 

710 mutual_friends = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user1.id)).mutual_friends 

711 assert len(mutual_friends) == 1 

712 assert mutual_friends[0].user_id == user3.id 

713 

714 # Check pending request doesn't have effect 

715 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user5.id)) 

716 

717 mutual_friends = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user1.id)).mutual_friends 

718 assert len(mutual_friends) == 1 

719 assert mutual_friends[0].user_id == user3.id 

720 

721 # both ways 

722 with api_session(token1) as api: 

723 mutual_friends = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user2.id)).mutual_friends 

724 assert len(mutual_friends) == 1 

725 assert mutual_friends[0].user_id == user3.id 

726 

727 

728def test_mutual_friends_self(db): 

729 user1, token1 = generate_user() 

730 user2, token2 = generate_user() 

731 user3, token3 = generate_user() 

732 user4, token4 = generate_user() 

733 

734 make_friends(user1, user2) 

735 make_friends(user2, user3) 

736 make_friends(user1, user4) 

737 

738 with api_session(token1) as api: 

739 res = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user1.id)) 

740 assert len(res.mutual_friends) == 0 

741 

742 with api_session(token2) as api: 

743 res = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user2.id)) 

744 assert len(res.mutual_friends) == 0 

745 

746 with api_session(token3) as api: 

747 res = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user3.id)) 

748 assert len(res.mutual_friends) == 0 

749 

750 with api_session(token4) as api: 

751 res = api.ListMutualFriends(api_pb2.ListMutualFriendsReq(user_id=user4.id)) 

752 assert len(res.mutual_friends) == 0 

753 

754 

755def test_CancelFriendRequest(db): 

756 user1, token1 = generate_user() 

757 user2, token2 = generate_user() 

758 

759 with api_session(token1) as api: 

760 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

761 

762 res = api.ListFriendRequests(empty_pb2.Empty()) 

763 assert res.sent[0].user_id == user2.id 

764 fr_id = res.sent[0].friend_request_id 

765 

766 api.CancelFriendRequest(api_pb2.CancelFriendRequestReq(friend_request_id=fr_id)) 

767 

768 # check it's gone 

769 res = api.ListFriendRequests(empty_pb2.Empty()) 

770 assert len(res.sent) == 0 

771 assert len(res.received) == 0 

772 

773 # check not friends 

774 res = api.ListFriends(empty_pb2.Empty()) 

775 assert len(res.user_ids) == 0 

776 

777 with api_session(token2) as api: 

778 # check it's gone 

779 res = api.ListFriendRequests(empty_pb2.Empty()) 

780 assert len(res.sent) == 0 

781 assert len(res.received) == 0 

782 

783 # check we're not friends 

784 res = api.ListFriends(empty_pb2.Empty()) 

785 assert len(res.user_ids) == 0 

786 

787 with api_session(token1) as api: 

788 # check we can send another friend req 

789 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

790 

791 res = api.ListFriendRequests(empty_pb2.Empty()) 

792 assert res.sent[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING 

793 assert res.sent[0].user_id == user2.id 

794 

795 

796def test_accept_friend_request(db): 

797 user1, token1 = generate_user() 

798 user2, token2 = generate_user() 

799 

800 with session_scope() as session: 

801 friend_request = FriendRelationship(from_user_id=user1.id, to_user_id=user2.id, status=FriendStatus.pending) 

802 session.add(friend_request) 

803 session.commit() 

804 friend_request_id = friend_request.id 

805 

806 with api_session(token2) as api: 

807 # check request pending 

808 res = api.ListFriendRequests(empty_pb2.Empty()) 

809 assert len(res.received) == 1 

810 assert res.received[0].user_id == user1.id 

811 

812 api.RespondFriendRequest(api_pb2.RespondFriendRequestReq(friend_request_id=friend_request_id, accept=True)) 

813 

814 # check request is gone 

815 res = api.ListFriendRequests(empty_pb2.Empty()) 

816 assert len(res.sent) == 0 

817 assert len(res.received) == 0 

818 

819 # check now friends 

820 res = api.ListFriends(empty_pb2.Empty()) 

821 assert len(res.user_ids) == 1 

822 assert res.user_ids[0] == user1.id 

823 

824 with api_session(token1) as api: 

825 # check request gone 

826 res = api.ListFriendRequests(empty_pb2.Empty()) 

827 assert len(res.sent) == 0 

828 assert len(res.received) == 0 

829 

830 # check now friends 

831 res = api.ListFriends(empty_pb2.Empty()) 

832 assert len(res.user_ids) == 1 

833 assert res.user_ids[0] == user2.id 

834 

835 

836def test_reject_friend_request(db): 

837 user1, token1 = generate_user() 

838 user2, token2 = generate_user() 

839 

840 with api_session(token1) as api: 

841 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

842 

843 res = api.ListFriendRequests(empty_pb2.Empty()) 

844 assert res.sent[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING 

845 assert res.sent[0].user_id == user2.id 

846 

847 with api_session(token2) as api: 

848 res = api.ListFriendRequests(empty_pb2.Empty()) 

849 assert res.received[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING 

850 assert res.received[0].user_id == user1.id 

851 

852 fr_id = res.received[0].friend_request_id 

853 

854 # reject it 

855 api.RespondFriendRequest(api_pb2.RespondFriendRequestReq(friend_request_id=fr_id, accept=False)) 

856 

857 # check it's gone 

858 res = api.ListFriendRequests(empty_pb2.Empty()) 

859 assert len(res.sent) == 0 

860 assert len(res.received) == 0 

861 

862 # check not friends 

863 res = api.ListFriends(empty_pb2.Empty()) 

864 assert len(res.user_ids) == 0 

865 

866 with api_session(token1) as api: 

867 # check it's gone 

868 res = api.ListFriendRequests(empty_pb2.Empty()) 

869 assert len(res.sent) == 0 

870 assert len(res.received) == 0 

871 

872 # check we're not friends 

873 res = api.ListFriends(empty_pb2.Empty()) 

874 assert len(res.user_ids) == 0 

875 

876 # check we can send another friend req 

877 api.SendFriendRequest(api_pb2.SendFriendRequestReq(user_id=user2.id)) 

878 

879 res = api.ListFriendRequests(empty_pb2.Empty()) 

880 assert res.sent[0].state == api_pb2.FriendRequest.FriendRequestStatus.PENDING 

881 assert res.sent[0].user_id == user2.id 

882 

883 

884def test_hosting_preferences(db): 

885 user1, token1 = generate_user() 

886 user2, token2 = generate_user() 

887 

888 with api_session(token1) as api: 

889 res = api.GetUser(api_pb2.GetUserReq(user=user1.username)) 

890 assert not res.HasField("max_guests") 

891 assert not res.HasField("last_minute") 

892 assert not res.HasField("has_pets") 

893 assert not res.HasField("accepts_pets") 

894 assert not res.HasField("pet_details") 

895 assert not res.HasField("has_kids") 

896 assert not res.HasField("accepts_kids") 

897 assert not res.HasField("kid_details") 

898 assert not res.HasField("has_housemates") 

899 assert not res.HasField("housemate_details") 

900 assert not res.HasField("wheelchair_accessible") 

901 assert res.smoking_allowed == api_pb2.SMOKING_LOCATION_UNKNOWN 

902 assert not res.HasField("smokes_at_home") 

903 assert not res.HasField("drinking_allowed") 

904 assert not res.HasField("drinks_at_home") 

905 assert not res.HasField("other_host_info") 

906 assert res.sleeping_arrangement == api_pb2.SLEEPING_ARRANGEMENT_UNKNOWN 

907 assert not res.HasField("sleeping_details") 

908 assert not res.HasField("area") 

909 assert not res.HasField("house_rules") 

910 assert not res.HasField("parking") 

911 assert res.parking_details == api_pb2.PARKING_DETAILS_UNKNOWN 

912 assert not res.HasField("camping_ok") 

913 

914 api.UpdateProfile( 

915 api_pb2.UpdateProfileReq( 

916 max_guests=api_pb2.NullableUInt32Value(value=3), 

917 last_minute=api_pb2.NullableBoolValue(value=True), 

918 has_pets=api_pb2.NullableBoolValue(value=False), 

919 accepts_pets=api_pb2.NullableBoolValue(value=True), 

920 pet_details=api_pb2.NullableStringValue(value="I love dogs"), 

921 has_kids=api_pb2.NullableBoolValue(value=False), 

922 accepts_kids=api_pb2.NullableBoolValue(value=True), 

923 kid_details=api_pb2.NullableStringValue(value="I hate kids"), 

924 has_housemates=api_pb2.NullableBoolValue(value=False), 

925 housemate_details=api_pb2.NullableStringValue(value="I have 7 housemates"), 

926 wheelchair_accessible=api_pb2.NullableBoolValue(value=True), 

927 smoking_allowed=api_pb2.SMOKING_LOCATION_WINDOW, 

928 area=api_pb2.NullableStringValue(value="area!"), 

929 smokes_at_home=api_pb2.NullableBoolValue(value=False), 

930 drinking_allowed=api_pb2.NullableBoolValue(value=True), 

931 drinks_at_home=api_pb2.NullableBoolValue(value=False), 

932 other_host_info=api_pb2.NullableStringValue(value="I'm pretty swell"), 

933 sleeping_arrangement=api_pb2.SLEEPING_ARRANGEMENT_COMMON, 

934 sleeping_details=api_pb2.NullableStringValue(value="Couch in living room"), 

935 house_rules=api_pb2.NullableStringValue(value="RULES!"), 

936 parking=api_pb2.NullableBoolValue(value=True), 

937 parking_details=api_pb2.PARKING_DETAILS_PAID_ONSITE, 

938 camping_ok=api_pb2.NullableBoolValue(value=False), 

939 ) 

940 ) 

941 

942 # Use a second user to view the hosting preferences just to check 

943 # that it is public information. 

944 with api_session(token2) as api: 

945 res = api.GetUser(api_pb2.GetUserReq(user=user1.username)) 

946 assert res.max_guests.value == 3 

947 assert res.last_minute.value 

948 assert not res.has_pets.value 

949 assert res.accepts_pets.value 

950 assert res.pet_details.value == "I love dogs" 

951 assert not res.has_kids.value 

952 assert res.accepts_kids.value 

953 assert res.kid_details.value == "I hate kids" 

954 assert not res.has_housemates.value 

955 assert res.housemate_details.value == "I have 7 housemates" 

956 assert res.wheelchair_accessible.value 

957 assert res.smoking_allowed == api_pb2.SMOKING_LOCATION_WINDOW 

958 assert not res.smokes_at_home.value 

959 assert res.drinking_allowed.value 

960 assert not res.drinks_at_home.value 

961 assert res.other_host_info.value == "I'm pretty swell" 

962 assert res.sleeping_arrangement == api_pb2.SLEEPING_ARRANGEMENT_COMMON 

963 assert res.sleeping_details.value == "Couch in living room" 

964 assert res.area.value == "area!" 

965 assert res.house_rules.value == "RULES!" 

966 assert res.parking.value 

967 assert res.parking_details == api_pb2.PARKING_DETAILS_PAID_ONSITE 

968 assert not res.camping_ok.value 

969 

970 # test unsetting 

971 with api_session(token1) as api: 

972 api.UpdateProfile( 

973 api_pb2.UpdateProfileReq( 

974 max_guests=api_pb2.NullableUInt32Value(is_null=True), 

975 last_minute=api_pb2.NullableBoolValue(is_null=True), 

976 has_pets=api_pb2.NullableBoolValue(is_null=True), 

977 accepts_pets=api_pb2.NullableBoolValue(is_null=True), 

978 pet_details=api_pb2.NullableStringValue(is_null=True), 

979 has_kids=api_pb2.NullableBoolValue(is_null=True), 

980 accepts_kids=api_pb2.NullableBoolValue(is_null=True), 

981 kid_details=api_pb2.NullableStringValue(is_null=True), 

982 has_housemates=api_pb2.NullableBoolValue(is_null=True), 

983 housemate_details=api_pb2.NullableStringValue(is_null=True), 

984 wheelchair_accessible=api_pb2.NullableBoolValue(is_null=True), 

985 smoking_allowed=api_pb2.SMOKING_LOCATION_UNKNOWN, 

986 area=api_pb2.NullableStringValue(is_null=True), 

987 smokes_at_home=api_pb2.NullableBoolValue(is_null=True), 

988 drinking_allowed=api_pb2.NullableBoolValue(is_null=True), 

989 drinks_at_home=api_pb2.NullableBoolValue(is_null=True), 

990 other_host_info=api_pb2.NullableStringValue(is_null=True), 

991 sleeping_arrangement=api_pb2.SLEEPING_ARRANGEMENT_UNKNOWN, 

992 sleeping_details=api_pb2.NullableStringValue(is_null=True), 

993 house_rules=api_pb2.NullableStringValue(is_null=True), 

994 parking=api_pb2.NullableBoolValue(is_null=True), 

995 parking_details=api_pb2.PARKING_DETAILS_UNKNOWN, 

996 camping_ok=api_pb2.NullableBoolValue(is_null=True), 

997 ) 

998 ) 

999 

1000 res = api.GetUser(api_pb2.GetUserReq(user=user1.username)) 

1001 assert not res.HasField("max_guests") 

1002 assert not res.HasField("last_minute") 

1003 assert not res.HasField("has_pets") 

1004 assert not res.HasField("accepts_pets") 

1005 assert not res.HasField("pet_details") 

1006 assert not res.HasField("has_kids") 

1007 assert not res.HasField("accepts_kids") 

1008 assert not res.HasField("kid_details") 

1009 assert not res.HasField("has_housemates") 

1010 assert not res.HasField("housemate_details") 

1011 assert not res.HasField("wheelchair_accessible") 

1012 assert res.smoking_allowed == api_pb2.SMOKING_LOCATION_UNKNOWN 

1013 assert not res.HasField("smokes_at_home") 

1014 assert not res.HasField("drinking_allowed") 

1015 assert not res.HasField("drinks_at_home") 

1016 assert not res.HasField("other_host_info") 

1017 assert res.sleeping_arrangement == api_pb2.SLEEPING_ARRANGEMENT_UNKNOWN 

1018 assert not res.HasField("sleeping_details") 

1019 assert not res.HasField("area") 

1020 assert not res.HasField("house_rules") 

1021 assert not res.HasField("parking") 

1022 assert res.parking_details == api_pb2.PARKING_DETAILS_UNKNOWN 

1023 assert not res.HasField("camping_ok") 

1024 

1025 

1026def test_badges(db): 

1027 user1, _ = generate_user() 

1028 user2, _ = generate_user() 

1029 user3, _ = generate_user() 

1030 user4, token = generate_user() 

1031 

1032 update_badges(empty_pb2.Empty()) 

1033 

1034 with api_session(token) as api: 

1035 assert api.GetUser(api_pb2.GetUserReq(user=user1.username)).badges == ["founder", "board_member"] 

1036 assert api.GetUser(api_pb2.GetUserReq(user=user2.username)).badges == ["founder", "board_member"] 

1037 assert api.GetUser(api_pb2.GetUserReq(user=user3.username)).badges == []