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

662 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-12-06 23:17 +0000

1from datetime import timedelta 

2 

3import grpc 

4import pytest 

5from geoalchemy2 import WKBElement 

6from google.protobuf import wrappers_pb2 

7from sqlalchemy.orm import Session 

8 

9from couchers.db import is_user_in_node_geography, session_scope 

10from couchers.materialized_views import refresh_materialized_views 

11from couchers.models import ( 

12 Cluster, 

13 ClusterRole, 

14 ClusterSubscription, 

15 Node, 

16 Page, 

17 PageType, 

18 PageVersion, 

19 SignupFlow, 

20 Thread, 

21) 

22from couchers.proto import api_pb2, auth_pb2, communities_pb2, discussions_pb2, events_pb2, pages_pb2 

23from couchers.sql import couchers_select as select 

24from couchers.tasks import enforce_community_memberships 

25from couchers.utils import Timestamp_from_datetime, create_coordinate, create_polygon_lat_lng, now, to_multi 

26from tests.test_auth import get_session_cookie_tokens 

27from tests.test_fixtures import ( # noqa 

28 auth_api_session, 

29 communities_session, 

30 db, 

31 discussions_session, 

32 events_session, 

33 generate_user, 

34 get_user_id_and_token, 

35 pages_session, 

36 testconfig, 

37) 

38 

39 

40@pytest.fixture(autouse=True) 

41def _(testconfig): 

42 pass 

43 

44 

45# For testing purposes, restrict ourselves to a 1D-world, consisting of "intervals" that have width 2, and coordinates 

46# that are points at (x, 1). 

47# we'll stick to EPSG4326, even though it's not ideal, so don't use too large values, but it's around the equator, so 

48# mostly fine 

49 

50 

51def create_1d_polygon(lb: int, ub: int) -> WKBElement: 

52 # given a lower bound and upper bound on x, creates the given interval 

53 return create_polygon_lat_lng([[lb, 0], [lb, 2], [ub, 2], [ub, 0], [lb, 0]]) 

54 

55 

56def create_1d_point(x: int) -> WKBElement: 

57 return create_coordinate(x, 1) 

58 

59 

60def create_community(session, interval_lb, interval_ub, name, admins, extra_members, parent): 

61 node = Node( 

62 geom=to_multi(create_1d_polygon(interval_lb, interval_ub)), 

63 parent_node=parent, 

64 ) 

65 session.add(node) 

66 cluster = Cluster( 

67 name=f"{name}", 

68 description=f"Description for {name}", 

69 parent_node=node, 

70 is_official_cluster=True, 

71 ) 

72 session.add(cluster) 

73 main_page = Page( 

74 parent_node=cluster.parent_node, 

75 creator_user_id=admins[0].id, 

76 owner_cluster=cluster, 

77 type=PageType.main_page, 

78 thread=Thread(), 

79 ) 

80 session.add(main_page) 

81 page_version = PageVersion( 

82 page=main_page, 

83 editor_user_id=admins[0].id, 

84 title=f"Main page for the {name} community", 

85 content="There is nothing here yet...", 

86 ) 

87 session.add(page_version) 

88 for admin in admins: 

89 cluster.cluster_subscriptions.append( 

90 ClusterSubscription( 

91 user_id=admin.id, 

92 role=ClusterRole.admin, 

93 ) 

94 ) 

95 for member in extra_members: 

96 cluster.cluster_subscriptions.append( 

97 ClusterSubscription( 

98 user_id=member.id, 

99 role=ClusterRole.member, 

100 ) 

101 ) 

102 session.commit() 

103 # other members will be added by enforce_community_memberships() 

104 return node 

105 

106 

107def create_group(session, name, admins, members, parent_community): 

108 cluster = Cluster( 

109 name=f"{name}", 

110 description=f"Description for {name}", 

111 parent_node=parent_community, 

112 ) 

113 session.add(cluster) 

114 main_page = Page( 

115 parent_node=cluster.parent_node, 

116 creator_user=admins[0], 

117 owner_cluster=cluster, 

118 type=PageType.main_page, 

119 thread=Thread(), 

120 ) 

121 session.add(main_page) 

122 page_version = PageVersion( 

123 page=main_page, 

124 editor_user=admins[0], 

125 title=f"Main page for the {name} community", 

126 content="There is nothing here yet...", 

127 ) 

128 session.add(page_version) 

129 for admin in admins: 

130 cluster.cluster_subscriptions.append( 

131 ClusterSubscription( 

132 user=admin, 

133 role=ClusterRole.admin, 

134 ) 

135 ) 

136 for member in members: 

137 cluster.cluster_subscriptions.append( 

138 ClusterSubscription( 

139 user=member, 

140 role=ClusterRole.member, 

141 ) 

142 ) 

143 session.commit() 

144 return cluster 

145 

146 

147def create_place(token, title, content, address, x): 

148 with pages_session(token) as api: 

149 res = api.CreatePlace( 

150 pages_pb2.CreatePlaceReq( 

151 title=title, 

152 content=content, 

153 address=address, 

154 location=pages_pb2.Coordinate( 

155 lat=x, 

156 lng=1, 

157 ), 

158 ) 

159 ) 

160 

161 

162def create_discussion(token, community_id, group_id, title, content): 

163 # set group_id or community_id to None 

164 with discussions_session(token) as api: 

165 res = api.CreateDiscussion( 

166 discussions_pb2.CreateDiscussionReq( 

167 title=title, 

168 content=content, 

169 owner_community_id=community_id, 

170 owner_group_id=group_id, 

171 ) 

172 ) 

173 

174 

175def create_event(token, community_id, group_id, title, content, start_td): 

176 with events_session(token) as api: 

177 res = api.CreateEvent( 

178 events_pb2.CreateEventReq( 

179 title=title, 

180 content=content, 

181 offline_information=events_pb2.OfflineEventInformation( 

182 address="Near Null Island", 

183 lat=0.1, 

184 lng=0.2, 

185 ), 

186 start_time=Timestamp_from_datetime(now() + start_td), 

187 end_time=Timestamp_from_datetime(now() + start_td + timedelta(hours=2)), 

188 timezone="UTC", 

189 ) 

190 ) 

191 api.TransferEvent( 

192 events_pb2.TransferEventReq( 

193 event_id=res.event_id, 

194 new_owner_community_id=community_id, 

195 new_owner_group_id=group_id, 

196 ) 

197 ) 

198 

199 

200def get_community_id(session: Session, community_name: str) -> int: 

201 return session.execute( 

202 select(Cluster.parent_node_id).where(Cluster.is_official_cluster).where(Cluster.name == community_name) 

203 ).scalar_one() 

204 

205 

206def get_group_id(session: Session, group_name: str) -> int: 

207 return session.execute( 

208 select(Cluster.id).where(~Cluster.is_official_cluster).where(Cluster.name == group_name) 

209 ).scalar_one() 

210 

211 

212@pytest.fixture(scope="class") 

213def testing_communities(db_class, testconfig): 

214 user1, token1 = generate_user(username="user1", geom=create_1d_point(1), geom_radius=0.1) 

215 user2, token2 = generate_user(username="user2", geom=create_1d_point(2), geom_radius=0.1) 

216 user3, token3 = generate_user(username="user3", geom=create_1d_point(3), geom_radius=0.1) 

217 user4, token4 = generate_user(username="user4", geom=create_1d_point(8), geom_radius=0.1) 

218 user5, token5 = generate_user(username="user5", geom=create_1d_point(6), geom_radius=0.1) 

219 user6, token6 = generate_user(username="user6", geom=create_1d_point(65), geom_radius=0.1) 

220 user7, token7 = generate_user(username="user7", geom=create_1d_point(80), geom_radius=0.1) 

221 user8, token8 = generate_user(username="user8", geom=create_1d_point(51), geom_radius=0.1) 

222 

223 with session_scope() as session: 

224 w = create_community(session, 0, 100, "Global", [user1, user3, user7], [], None) 

225 c2 = create_community(session, 52, 100, "Country 2", [user6, user7], [], w) 

226 c2r1 = create_community(session, 52, 71, "Country 2, Region 1", [user6], [user8], c2) 

227 c2r1c1 = create_community(session, 53, 70, "Country 2, Region 1, City 1", [user8], [], c2r1) 

228 c1 = create_community(session, 0, 50, "Country 1", [user1, user2], [], w) 

229 c1r1 = create_community(session, 0, 10, "Country 1, Region 1", [user1, user2], [], c1) 

230 c1r1c1 = create_community(session, 0, 5, "Country 1, Region 1, City 1", [user2], [], c1r1) 

231 c1r1c2 = create_community(session, 7, 10, "Country 1, Region 1, City 2", [user4, user5], [user2], c1r1) 

232 c1r2 = create_community(session, 20, 25, "Country 1, Region 2", [user2], [], c1) 

233 c1r2c1 = create_community(session, 21, 23, "Country 1, Region 2, City 1", [user2], [], c1r2) 

234 

235 h = create_group(session, "Hitchhikers", [user1, user2], [user5, user8], w) 

236 create_group(session, "Country 1, Region 1, Foodies", [user1], [user2, user4], c1r1) 

237 create_group(session, "Country 1, Region 1, Skaters", [user2], [user1], c1r1) 

238 create_group(session, "Country 1, Region 2, Foodies", [user2], [user4, user5], c1r2) 

239 create_group(session, "Country 2, Region 1, Foodies", [user6], [user7], c2r1) 

240 

241 w_id = w.id 

242 c1r1c2_id = c1r1c2.id 

243 h_id = h.id 

244 c1_id = c1.id 

245 

246 create_discussion(token1, w_id, None, "Discussion title 1", "Discussion content 1") 

247 create_discussion(token3, w_id, None, "Discussion title 2", "Discussion content 2") 

248 create_discussion(token3, w_id, None, "Discussion title 3", "Discussion content 3") 

249 create_discussion(token3, w_id, None, "Discussion title 4", "Discussion content 4") 

250 create_discussion(token3, w_id, None, "Discussion title 5", "Discussion content 5") 

251 create_discussion(token3, w_id, None, "Discussion title 6", "Discussion content 6") 

252 create_discussion(token4, c1r1c2_id, None, "Discussion title 7", "Discussion content 7") 

253 create_discussion(token5, None, h_id, "Discussion title 8", "Discussion content 8") 

254 create_discussion(token1, None, h_id, "Discussion title 9", "Discussion content 9") 

255 create_discussion(token2, None, h_id, "Discussion title 10", "Discussion content 10") 

256 create_discussion(token3, None, h_id, "Discussion title 11", "Discussion content 11") 

257 create_discussion(token4, None, h_id, "Discussion title 12", "Discussion content 12") 

258 create_discussion(token5, None, h_id, "Discussion title 13", "Discussion content 13") 

259 create_discussion(token8, None, h_id, "Discussion title 14", "Discussion content 14") 

260 

261 create_event(token3, c1_id, None, "Event title 1", "Event content 1", timedelta(hours=1)) 

262 create_event(token1, c1_id, None, "Event title 2", "Event content 2", timedelta(hours=2)) 

263 create_event(token3, c1_id, None, "Event title 3", "Event content 3", timedelta(hours=3)) 

264 create_event(token1, c1_id, None, "Event title 4", "Event content 4", timedelta(hours=4)) 

265 create_event(token3, c1_id, None, "Event title 5", "Event content 5", timedelta(hours=5)) 

266 create_event(token1, c1_id, None, "Event title 6", "Event content 6", timedelta(hours=6)) 

267 create_event(token2, None, h_id, "Event title 7", "Event content 7", timedelta(hours=7)) 

268 create_event(token2, None, h_id, "Event title 8", "Event content 8", timedelta(hours=8)) 

269 create_event(token2, None, h_id, "Event title 9", "Event content 9", timedelta(hours=9)) 

270 create_event(token2, None, h_id, "Event title 10", "Event content 10", timedelta(hours=10)) 

271 create_event(token2, None, h_id, "Event title 11", "Event content 11", timedelta(hours=11)) 

272 create_event(token2, None, h_id, "Event title 12", "Event content 12", timedelta(hours=12)) 

273 

274 enforce_community_memberships() 

275 

276 create_place(token1, "Country 1, Region 1, Attraction", "Place content", "Somewhere in c1r1", 6) 

277 create_place(token2, "Country 1, Region 1, City 1, Attraction 1", "Place content", "Somewhere in c1r1c1", 3) 

278 create_place(token2, "Country 1, Region 1, City 1, Attraction 2", "Place content", "Somewhere in c1r1c1", 4) 

279 create_place(token8, "Global, Attraction", "Place content", "Somewhere in w", 51.5) 

280 create_place(token6, "Country 2, Region 1, Attraction", "Place content", "Somewhere in c2r1", 59) 

281 

282 refresh_materialized_views(None) 

283 

284 yield 

285 

286 

287class TestCommunities: 

288 @staticmethod 

289 def test_GetCommunity(testing_communities): 

290 with session_scope() as session: 

291 user1_id, token1 = get_user_id_and_token(session, "user1") 

292 user2_id, token2 = get_user_id_and_token(session, "user2") 

293 user6_id, token6 = get_user_id_and_token(session, "user6") 

294 w_id = get_community_id(session, "Global") 

295 c1_id = get_community_id(session, "Country 1") 

296 c1r1_id = get_community_id(session, "Country 1, Region 1") 

297 c1r1c1_id = get_community_id(session, "Country 1, Region 1, City 1") 

298 c2_id = get_community_id(session, "Country 2") 

299 

300 with communities_session(token2) as api: 

301 res = api.GetCommunity( 

302 communities_pb2.GetCommunityReq( 

303 community_id=w_id, 

304 ) 

305 ) 

306 assert res.name == "Global" 

307 assert res.slug == "global" 

308 assert res.description == "Description for Global" 

309 assert len(res.parents) == 1 

310 assert res.parents[0].HasField("community") 

311 assert res.parents[0].community.community_id == w_id 

312 assert res.parents[0].community.name == "Global" 

313 assert res.parents[0].community.slug == "global" 

314 assert res.parents[0].community.description == "Description for Global" 

315 assert res.main_page.type == pages_pb2.PAGE_TYPE_MAIN_PAGE 

316 assert res.main_page.slug == "main-page-for-the-global-community" 

317 assert res.main_page.last_editor_user_id == user1_id 

318 assert res.main_page.creator_user_id == user1_id 

319 assert res.main_page.owner_community_id == w_id 

320 assert res.main_page.title == "Main page for the Global community" 

321 assert res.main_page.content == "There is nothing here yet..." 

322 assert not res.main_page.can_edit 

323 assert not res.main_page.can_moderate 

324 assert res.main_page.editor_user_ids == [user1_id] 

325 assert res.member 

326 assert not res.admin 

327 assert res.member_count == 8 

328 assert res.admin_count == 3 

329 

330 res = api.GetCommunity( 

331 communities_pb2.GetCommunityReq( 

332 community_id=c1r1c1_id, 

333 ) 

334 ) 

335 assert res.community_id == c1r1c1_id 

336 assert res.name == "Country 1, Region 1, City 1" 

337 assert res.slug == "country-1-region-1-city-1" 

338 assert res.description == "Description for Country 1, Region 1, City 1" 

339 assert len(res.parents) == 4 

340 assert res.parents[0].HasField("community") 

341 assert res.parents[0].community.community_id == w_id 

342 assert res.parents[0].community.name == "Global" 

343 assert res.parents[0].community.slug == "global" 

344 assert res.parents[0].community.description == "Description for Global" 

345 assert res.parents[1].HasField("community") 

346 assert res.parents[1].community.community_id == c1_id 

347 assert res.parents[1].community.name == "Country 1" 

348 assert res.parents[1].community.slug == "country-1" 

349 assert res.parents[1].community.description == "Description for Country 1" 

350 assert res.parents[2].HasField("community") 

351 assert res.parents[2].community.community_id == c1r1_id 

352 assert res.parents[2].community.name == "Country 1, Region 1" 

353 assert res.parents[2].community.slug == "country-1-region-1" 

354 assert res.parents[2].community.description == "Description for Country 1, Region 1" 

355 assert res.parents[3].HasField("community") 

356 assert res.parents[3].community.community_id == c1r1c1_id 

357 assert res.parents[3].community.name == "Country 1, Region 1, City 1" 

358 assert res.parents[3].community.slug == "country-1-region-1-city-1" 

359 assert res.parents[3].community.description == "Description for Country 1, Region 1, City 1" 

360 assert res.main_page.type == pages_pb2.PAGE_TYPE_MAIN_PAGE 

361 assert res.main_page.slug == "main-page-for-the-country-1-region-1-city-1-community" 

362 assert res.main_page.last_editor_user_id == user2_id 

363 assert res.main_page.creator_user_id == user2_id 

364 assert res.main_page.owner_community_id == c1r1c1_id 

365 assert res.main_page.title == "Main page for the Country 1, Region 1, City 1 community" 

366 assert res.main_page.content == "There is nothing here yet..." 

367 assert res.main_page.can_edit 

368 assert res.main_page.can_moderate 

369 assert res.main_page.editor_user_ids == [user2_id] 

370 assert res.member 

371 assert res.admin 

372 assert res.member_count == 3 

373 assert res.admin_count == 1 

374 

375 res = api.GetCommunity( 

376 communities_pb2.GetCommunityReq( 

377 community_id=c2_id, 

378 ) 

379 ) 

380 assert res.community_id == c2_id 

381 assert res.name == "Country 2" 

382 assert res.slug == "country-2" 

383 assert res.description == "Description for Country 2" 

384 assert len(res.parents) == 2 

385 assert res.parents[0].HasField("community") 

386 assert res.parents[0].community.community_id == w_id 

387 assert res.parents[0].community.name == "Global" 

388 assert res.parents[0].community.slug == "global" 

389 assert res.parents[0].community.description == "Description for Global" 

390 assert res.parents[1].HasField("community") 

391 assert res.parents[1].community.community_id == c2_id 

392 assert res.parents[1].community.name == "Country 2" 

393 assert res.parents[1].community.slug == "country-2" 

394 assert res.parents[1].community.description == "Description for Country 2" 

395 assert res.main_page.type == pages_pb2.PAGE_TYPE_MAIN_PAGE 

396 assert res.main_page.slug == "main-page-for-the-country-2-community" 

397 assert res.main_page.last_editor_user_id == user6_id 

398 assert res.main_page.creator_user_id == user6_id 

399 assert res.main_page.owner_community_id == c2_id 

400 assert res.main_page.title == "Main page for the Country 2 community" 

401 assert res.main_page.content == "There is nothing here yet..." 

402 assert not res.main_page.can_edit 

403 assert not res.main_page.can_moderate 

404 assert res.main_page.editor_user_ids == [user6_id] 

405 assert not res.member 

406 assert not res.admin 

407 assert res.member_count == 2 

408 assert res.admin_count == 2 

409 

410 @staticmethod 

411 def test_ListCommunities(testing_communities): 

412 with session_scope() as session: 

413 user1_id, token1 = get_user_id_and_token(session, "user1") 

414 c1_id = get_community_id(session, "Country 1") 

415 c1r1_id = get_community_id(session, "Country 1, Region 1") 

416 c1r2_id = get_community_id(session, "Country 1, Region 2") 

417 

418 with communities_session(token1) as api: 

419 res = api.ListCommunities( 

420 communities_pb2.ListCommunitiesReq( 

421 community_id=c1_id, 

422 ) 

423 ) 

424 assert [c.community_id for c in res.communities] == [c1r1_id, c1r2_id] 

425 

426 @staticmethod 

427 def test_ListCommunities_all(testing_communities): 

428 with session_scope() as session: 

429 user1_id, token1 = get_user_id_and_token(session, "user1") 

430 w_id = get_community_id(session, "Global") 

431 c1_id = get_community_id(session, "Country 1") 

432 c1r1_id = get_community_id(session, "Country 1, Region 1") 

433 c1r1c1_id = get_community_id(session, "Country 1, Region 1, City 1") 

434 c1r1c2_id = get_community_id(session, "Country 1, Region 1, City 2") 

435 c1r2_id = get_community_id(session, "Country 1, Region 2") 

436 c1r2c1_id = get_community_id(session, "Country 1, Region 2, City 1") 

437 c2_id = get_community_id(session, "Country 2") 

438 c2r1_id = get_community_id(session, "Country 2, Region 1") 

439 c2r1c1_id = get_community_id(session, "Country 2, Region 1, City 1") 

440 

441 # Fetch all communities ordered by name 

442 with communities_session(token1) as api: 

443 res = api.ListCommunities( 

444 communities_pb2.ListCommunitiesReq( 

445 page_size=5, 

446 ) 

447 ) 

448 assert [c.community_id for c in res.communities] == [c1_id, c1r1_id, c1r1c1_id, c1r1c2_id, c1r2_id] 

449 res = api.ListCommunities( 

450 communities_pb2.ListCommunitiesReq( 

451 page_size=2, 

452 page_token=res.next_page_token, 

453 ) 

454 ) 

455 assert [c.community_id for c in res.communities] == [c1r2c1_id, c2_id] 

456 res = api.ListCommunities( 

457 communities_pb2.ListCommunitiesReq( 

458 page_size=5, 

459 page_token=res.next_page_token, 

460 ) 

461 ) 

462 assert [c.community_id for c in res.communities] == [c2r1_id, c2r1c1_id, w_id] 

463 

464 @staticmethod 

465 def test_ListUserCommunities(testing_communities): 

466 with session_scope() as session: 

467 user2_id, token2 = get_user_id_and_token(session, "user2") 

468 w_id = get_community_id(session, "Global") 

469 c1_id = get_community_id(session, "Country 1") 

470 c1r1_id = get_community_id(session, "Country 1, Region 1") 

471 c1r1c1_id = get_community_id(session, "Country 1, Region 1, City 1") 

472 c1r1c2_id = get_community_id(session, "Country 1, Region 1, City 2") 

473 c1r2_id = get_community_id(session, "Country 1, Region 2") 

474 c1r2c1_id = get_community_id(session, "Country 1, Region 2, City 1") 

475 

476 # Fetch user2's communities from user2's account 

477 with communities_session(token2) as api: 

478 res = api.ListUserCommunities(communities_pb2.ListUserCommunitiesReq()) 

479 assert [c.community_id for c in res.communities] == [ 

480 w_id, 

481 c1_id, 

482 c1r1_id, 

483 c1r1c1_id, 

484 c1r1c2_id, 

485 c1r2_id, 

486 c1r2c1_id, 

487 ] 

488 

489 @staticmethod 

490 def test_ListOtherUserCommunities(testing_communities): 

491 with session_scope() as session: 

492 user1_id, token1 = get_user_id_and_token(session, "user1") 

493 user2_id, token2 = get_user_id_and_token(session, "user2") 

494 w_id = get_community_id(session, "Global") 

495 c1_id = get_community_id(session, "Country 1") 

496 c1r1_id = get_community_id(session, "Country 1, Region 1") 

497 c1r1c1_id = get_community_id(session, "Country 1, Region 1, City 1") 

498 c1r1c2_id = get_community_id(session, "Country 1, Region 1, City 2") 

499 c1r2_id = get_community_id(session, "Country 1, Region 2") 

500 c1r2c1_id = get_community_id(session, "Country 1, Region 2, City 1") 

501 

502 # Fetch user2's communities from user1's account 

503 with communities_session(token1) as api: 

504 res = api.ListUserCommunities(communities_pb2.ListUserCommunitiesReq(user_id=user2_id)) 

505 assert [c.community_id for c in res.communities] == [ 

506 w_id, 

507 c1_id, 

508 c1r1_id, 

509 c1r1c1_id, 

510 c1r1c2_id, 

511 c1r2_id, 

512 c1r2c1_id, 

513 ] 

514 

515 @staticmethod 

516 def test_ListGroups(testing_communities): 

517 with session_scope() as session: 

518 user1_id, token1 = get_user_id_and_token(session, "user1") 

519 user5_id, token5 = get_user_id_and_token(session, "user5") 

520 w_id = get_community_id(session, "Global") 

521 hitchhikers_id = get_group_id(session, "Hitchhikers") 

522 c1r1_id = get_community_id(session, "Country 1, Region 1") 

523 foodies_id = get_group_id(session, "Country 1, Region 1, Foodies") 

524 skaters_id = get_group_id(session, "Country 1, Region 1, Skaters") 

525 

526 with communities_session(token1) as api: 

527 res = api.ListGroups( 

528 communities_pb2.ListGroupsReq( 

529 community_id=c1r1_id, 

530 ) 

531 ) 

532 assert [g.group_id for g in res.groups] == [foodies_id, skaters_id] 

533 

534 with communities_session(token5) as api: 

535 res = api.ListGroups( 

536 communities_pb2.ListGroupsReq( 

537 community_id=w_id, 

538 ) 

539 ) 

540 assert len(res.groups) == 1 

541 assert res.groups[0].group_id == hitchhikers_id 

542 

543 @staticmethod 

544 def test_ListAdmins(testing_communities): 

545 with session_scope() as session: 

546 user1_id, token1 = get_user_id_and_token(session, "user1") 

547 user3_id, token3 = get_user_id_and_token(session, "user3") 

548 user4_id, token4 = get_user_id_and_token(session, "user4") 

549 user5_id, token5 = get_user_id_and_token(session, "user5") 

550 user7_id, token7 = get_user_id_and_token(session, "user7") 

551 w_id = get_community_id(session, "Global") 

552 c1r1c2_id = get_community_id(session, "Country 1, Region 1, City 2") 

553 

554 with communities_session(token1) as api: 

555 res = api.ListAdmins( 

556 communities_pb2.ListAdminsReq( 

557 community_id=w_id, 

558 ) 

559 ) 

560 assert res.admin_user_ids == [user1_id, user3_id, user7_id] 

561 

562 res = api.ListAdmins( 

563 communities_pb2.ListAdminsReq( 

564 community_id=c1r1c2_id, 

565 ) 

566 ) 

567 assert res.admin_user_ids == [user4_id, user5_id] 

568 

569 @staticmethod 

570 def test_AddAdmin(testing_communities): 

571 with session_scope() as session: 

572 user4_id, token4 = get_user_id_and_token(session, "user4") 

573 user5_id, _ = get_user_id_and_token(session, "user5") 

574 user2_id, _ = get_user_id_and_token(session, "user2") 

575 user8_id, token8 = get_user_id_and_token(session, "user8") 

576 node_id = get_community_id(session, "Country 1, Region 1, City 2") 

577 

578 with communities_session(token8) as api: 

579 with pytest.raises(grpc.RpcError) as err: 

580 api.AddAdmin(communities_pb2.AddAdminReq(community_id=node_id, user_id=user2_id)) 

581 assert err.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

582 assert err.value.details() == "You're not allowed to moderate that community" 

583 

584 with communities_session(token4) as api: 

585 res = api.ListAdmins(communities_pb2.ListAdminsReq(community_id=node_id)) 

586 assert res.admin_user_ids == [user4_id, user5_id] 

587 

588 with pytest.raises(grpc.RpcError) as err: 

589 api.AddAdmin(communities_pb2.AddAdminReq(community_id=node_id, user_id=user8_id)) 

590 assert err.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

591 assert err.value.details() == "That user is not in the community." 

592 

593 with pytest.raises(grpc.RpcError) as err: 

594 api.AddAdmin(communities_pb2.AddAdminReq(community_id=node_id, user_id=user5_id)) 

595 assert err.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

596 assert err.value.details() == "That user is already an admin." 

597 

598 api.AddAdmin(communities_pb2.AddAdminReq(community_id=node_id, user_id=user2_id)) 

599 res = api.ListAdmins(communities_pb2.ListAdminsReq(community_id=node_id)) 

600 assert res.admin_user_ids == [user2_id, user4_id, user5_id] 

601 # Cleanup because database changes do not roll back 

602 api.RemoveAdmin(communities_pb2.RemoveAdminReq(community_id=node_id, user_id=user2_id)) 

603 

604 @staticmethod 

605 def test_RemoveAdmin(testing_communities): 

606 with session_scope() as session: 

607 user4_id, token4 = get_user_id_and_token(session, "user4") 

608 user5_id, _ = get_user_id_and_token(session, "user5") 

609 user2_id, _ = get_user_id_and_token(session, "user2") 

610 user8_id, token8 = get_user_id_and_token(session, "user8") 

611 node_id = get_community_id(session, "Country 1, Region 1, City 2") 

612 

613 with communities_session(token8) as api: 

614 with pytest.raises(grpc.RpcError) as err: 

615 api.AddAdmin(communities_pb2.AddAdminReq(community_id=node_id, user_id=user2_id)) 

616 assert err.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

617 assert err.value.details() == "You're not allowed to moderate that community" 

618 

619 with communities_session(token4) as api: 

620 res = api.ListAdmins(communities_pb2.ListAdminsReq(community_id=node_id)) 

621 assert res.admin_user_ids == [user4_id, user5_id] 

622 

623 with pytest.raises(grpc.RpcError) as err: 

624 api.RemoveAdmin(communities_pb2.RemoveAdminReq(community_id=node_id, user_id=user8_id)) 

625 assert err.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

626 assert err.value.details() == "That user is not in the community." 

627 

628 with pytest.raises(grpc.RpcError) as err: 

629 api.RemoveAdmin(communities_pb2.RemoveAdminReq(community_id=node_id, user_id=user2_id)) 

630 assert err.value.code() == grpc.StatusCode.FAILED_PRECONDITION 

631 assert err.value.details() == "That user is not an admin." 

632 

633 api.RemoveAdmin(communities_pb2.RemoveAdminReq(community_id=node_id, user_id=user5_id)) 

634 res = api.ListAdmins(communities_pb2.ListAdminsReq(community_id=node_id)) 

635 assert res.admin_user_ids == [user4_id] 

636 # Cleanup because database changes do not roll back 

637 api.AddAdmin(communities_pb2.AddAdminReq(community_id=node_id, user_id=user5_id)) 

638 

639 @staticmethod 

640 def test_ListMembers(testing_communities): 

641 with session_scope() as session: 

642 user1_id, token1 = get_user_id_and_token(session, "user1") 

643 user2_id, token2 = get_user_id_and_token(session, "user2") 

644 user3_id, token3 = get_user_id_and_token(session, "user3") 

645 user4_id, token4 = get_user_id_and_token(session, "user4") 

646 user5_id, token5 = get_user_id_and_token(session, "user5") 

647 user6_id, token6 = get_user_id_and_token(session, "user6") 

648 user7_id, token7 = get_user_id_and_token(session, "user7") 

649 user8_id, token8 = get_user_id_and_token(session, "user8") 

650 w_id = get_community_id(session, "Global") 

651 c1r1c2_id = get_community_id(session, "Country 1, Region 1, City 2") 

652 

653 with communities_session(token1) as api: 

654 res = api.ListMembers( 

655 communities_pb2.ListMembersReq( 

656 community_id=w_id, 

657 ) 

658 ) 

659 assert res.member_user_ids == [ 

660 user8_id, 

661 user7_id, 

662 user6_id, 

663 user5_id, 

664 user4_id, 

665 user3_id, 

666 user2_id, 

667 user1_id, 

668 ] 

669 

670 res = api.ListMembers( 

671 communities_pb2.ListMembersReq( 

672 community_id=c1r1c2_id, 

673 ) 

674 ) 

675 assert res.member_user_ids == [user5_id, user4_id, user2_id] 

676 

677 @staticmethod 

678 def test_ListNearbyUsers(testing_communities): 

679 with session_scope() as session: 

680 user1_id, token1 = get_user_id_and_token(session, "user1") 

681 user2_id, token2 = get_user_id_and_token(session, "user2") 

682 user3_id, token3 = get_user_id_and_token(session, "user3") 

683 user4_id, token4 = get_user_id_and_token(session, "user4") 

684 user5_id, token5 = get_user_id_and_token(session, "user5") 

685 user6_id, token6 = get_user_id_and_token(session, "user6") 

686 user7_id, token7 = get_user_id_and_token(session, "user7") 

687 user8_id, token8 = get_user_id_and_token(session, "user8") 

688 w_id = get_community_id(session, "Global") 

689 c1r1c2_id = get_community_id(session, "Country 1, Region 1, City 2") 

690 

691 with communities_session(token1) as api: 

692 res = api.ListNearbyUsers( 

693 communities_pb2.ListNearbyUsersReq( 

694 community_id=w_id, 

695 ) 

696 ) 

697 assert res.nearby_user_ids == [ 

698 user1_id, 

699 user2_id, 

700 user3_id, 

701 user4_id, 

702 user5_id, 

703 user6_id, 

704 user7_id, 

705 user8_id, 

706 ] 

707 

708 res = api.ListNearbyUsers( 

709 communities_pb2.ListNearbyUsersReq( 

710 community_id=c1r1c2_id, 

711 ) 

712 ) 

713 assert res.nearby_user_ids == [user4_id] 

714 

715 @staticmethod 

716 def test_ListDiscussions(testing_communities): 

717 with session_scope() as session: 

718 user1_id, token1 = get_user_id_and_token(session, "user1") 

719 w_id = get_community_id(session, "Global") 

720 c1r1c2_id = get_community_id(session, "Country 1, Region 1, City 2") 

721 

722 with communities_session(token1) as api: 

723 res = api.ListDiscussions( 

724 communities_pb2.ListDiscussionsReq( 

725 community_id=w_id, 

726 page_size=3, 

727 ) 

728 ) 

729 assert [d.title for d in res.discussions] == [ 

730 "Discussion title 6", 

731 "Discussion title 5", 

732 "Discussion title 4", 

733 ] 

734 for d in res.discussions: 

735 assert d.thread.thread_id > 0 

736 assert d.thread.num_responses == 0 

737 

738 res = api.ListDiscussions( 

739 communities_pb2.ListDiscussionsReq( 

740 community_id=w_id, 

741 page_token=res.next_page_token, 

742 page_size=2, 

743 ) 

744 ) 

745 assert [d.title for d in res.discussions] == [ 

746 "Discussion title 3", 

747 "Discussion title 2", 

748 ] 

749 for d in res.discussions: 

750 assert d.thread.thread_id > 0 

751 assert d.thread.num_responses == 0 

752 

753 res = api.ListDiscussions( 

754 communities_pb2.ListDiscussionsReq( 

755 community_id=w_id, 

756 page_token=res.next_page_token, 

757 page_size=2, 

758 ) 

759 ) 

760 assert [d.title for d in res.discussions] == [ 

761 "Discussion title 1", 

762 ] 

763 for d in res.discussions: 

764 assert d.thread.thread_id > 0 

765 assert d.thread.num_responses == 0 

766 

767 res = api.ListDiscussions( 

768 communities_pb2.ListDiscussionsReq( 

769 community_id=c1r1c2_id, 

770 ) 

771 ) 

772 assert [d.title for d in res.discussions] == [ 

773 "Discussion title 7", 

774 ] 

775 for d in res.discussions: 

776 assert d.thread.thread_id > 0 

777 assert d.thread.num_responses == 0 

778 

779 @staticmethod 

780 def test_is_user_in_node_geography(testing_communities): 

781 with session_scope() as session: 

782 c1_id = get_community_id(session, "Country 1") 

783 

784 user1_id, _ = get_user_id_and_token(session, "user1") 

785 user2_id, _ = get_user_id_and_token(session, "user2") 

786 user3_id, _ = get_user_id_and_token(session, "user3") 

787 user4_id, _ = get_user_id_and_token(session, "user4") 

788 user5_id, _ = get_user_id_and_token(session, "user5") 

789 

790 # All these users should be in Country 1's geography 

791 assert is_user_in_node_geography(session, user1_id, c1_id) 

792 assert is_user_in_node_geography(session, user2_id, c1_id) 

793 assert is_user_in_node_geography(session, user3_id, c1_id) 

794 assert is_user_in_node_geography(session, user4_id, c1_id) 

795 assert is_user_in_node_geography(session, user5_id, c1_id) 

796 

797 @staticmethod 

798 def test_ListEvents(testing_communities): 

799 with session_scope() as session: 

800 user1_id, token1 = get_user_id_and_token(session, "user1") 

801 c1_id = get_community_id(session, "Country 1") 

802 

803 with communities_session(token1) as api: 

804 res = api.ListEvents( 

805 communities_pb2.ListEventsReq( 

806 community_id=c1_id, 

807 page_size=3, 

808 ) 

809 ) 

810 assert [d.title for d in res.events] == [ 

811 "Event title 1", 

812 "Event title 2", 

813 "Event title 3", 

814 ] 

815 

816 res = api.ListEvents( 

817 communities_pb2.ListEventsReq( 

818 community_id=c1_id, 

819 page_token=res.next_page_token, 

820 page_size=2, 

821 ) 

822 ) 

823 assert [d.title for d in res.events] == [ 

824 "Event title 4", 

825 "Event title 5", 

826 ] 

827 

828 res = api.ListEvents( 

829 communities_pb2.ListEventsReq( 

830 community_id=c1_id, 

831 page_token=res.next_page_token, 

832 page_size=2, 

833 ) 

834 ) 

835 assert [d.title for d in res.events] == [ 

836 "Event title 6", 

837 ] 

838 assert not res.next_page_token 

839 

840 @staticmethod 

841 def test_empty_query_aborts(testing_communities): 

842 with session_scope() as session: 

843 _, token = get_user_id_and_token(session, "user1") 

844 

845 with communities_session(token) as api: 

846 with pytest.raises(grpc.RpcError) as err: 

847 api.SearchCommunities(communities_pb2.SearchCommunitiesReq(query=" ")) 

848 assert err.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

849 assert err.value.details() == "Query must be at least 3 characters long." 

850 

851 @staticmethod 

852 def test_min_length_lt_3_aborts(testing_communities): 

853 """ 

854 len(query) < 3 → return INVALID_ARGUMENT: query_too_short 

855 """ 

856 with session_scope() as session: 

857 _, token = get_user_id_and_token(session, "user1") 

858 

859 with communities_session(token) as api: 

860 with pytest.raises(grpc.RpcError) as err: 

861 api.SearchCommunities(communities_pb2.SearchCommunitiesReq(query="zz", page_size=5)) 

862 assert err.value.code() == grpc.StatusCode.INVALID_ARGUMENT 

863 assert err.value.details() == "Query must be at least 3 characters long." 

864 

865 @staticmethod 

866 def test_typo_matches_existing_name(testing_communities): 

867 """ 

868 Word_similarity should match a simple typo in community name. 

869 """ 

870 with session_scope() as session: 

871 _, token = get_user_id_and_token(session, "user1") 

872 c1_id = get_community_id(session, "Country 1") 

873 

874 with communities_session(token) as api: 

875 res = api.SearchCommunities(communities_pb2.SearchCommunitiesReq(query="Coutri 1", page_size=5)) 

876 ids = [c.community_id for c in res.communities] 

877 assert c1_id in ids 

878 

879 @staticmethod 

880 def test_word_similarity_matches_partial_word(testing_communities): 

881 """ 

882 Query 'city' should match 'Country 1, Region 1, City 1'. 

883 """ 

884 with session_scope() as session: 

885 _, token = get_user_id_and_token(session, "user1") 

886 city1_id = get_community_id(session, "Country 1, Region 1, City 1") # переименовал для ясности 

887 

888 with communities_session(token) as api: 

889 res = api.SearchCommunities(communities_pb2.SearchCommunitiesReq(query="city", page_size=5)) 

890 ids = [c.community_id for c in res.communities] 

891 assert city1_id in ids 

892 

893 @staticmethod 

894 def test_results_sorted_by_similarity(testing_communities): 

895 """ 

896 Results should be ordered by similarity score (best match first). 

897 For query 'Country 1, Region', the full region name should rank higher 

898 than deeper descendants like 'City 1'. 

899 """ 

900 with session_scope() as session: 

901 _, token = get_user_id_and_token(session, "user1") 

902 region_id = get_community_id(session, "Country 1, Region 1") 

903 city_id = get_community_id(session, "Country 1, Region 1, City 1") 

904 

905 with communities_session(token) as api: 

906 res = api.SearchCommunities(communities_pb2.SearchCommunitiesReq(query="Country 1, Region", page_size=5)) 

907 ids = [c.community_id for c in res.communities] 

908 

909 assert region_id in ids 

910 assert city_id in ids 

911 assert ids.index(region_id) < ids.index(city_id) 

912 

913 @staticmethod 

914 def test_no_results_returns_empty(testing_communities): 

915 """ 

916 For a nonsense query that shouldn't meet the similarity threshold, return empty list. 

917 """ 

918 with session_scope() as session: 

919 _, token = get_user_id_and_token(session, "user1") 

920 

921 with communities_session(token) as api: 

922 res = api.SearchCommunities(communities_pb2.SearchCommunitiesReq(query="qwertyuiopasdf", page_size=5)) 

923 assert res.communities == [] 

924 

925 @staticmethod 

926 def test_ListAllCommunities(testing_communities): 

927 """ 

928 Test that ListAllCommunities returns all communities with proper hierarchy information. 

929 """ 

930 with session_scope() as session: 

931 user1_id, token1 = get_user_id_and_token(session, "user1") 

932 user2_id, token2 = get_user_id_and_token(session, "user2") 

933 user6_id, token6 = get_user_id_and_token(session, "user6") 

934 w_id = get_community_id(session, "Global") 

935 c1_id = get_community_id(session, "Country 1") 

936 c1r1_id = get_community_id(session, "Country 1, Region 1") 

937 c1r1c1_id = get_community_id(session, "Country 1, Region 1, City 1") 

938 c1r1c2_id = get_community_id(session, "Country 1, Region 1, City 2") 

939 c1r2_id = get_community_id(session, "Country 1, Region 2") 

940 c1r2c1_id = get_community_id(session, "Country 1, Region 2, City 1") 

941 c2_id = get_community_id(session, "Country 2") 

942 c2r1_id = get_community_id(session, "Country 2, Region 1") 

943 c2r1c1_id = get_community_id(session, "Country 2, Region 1, City 1") 

944 

945 # Test with user1 who is a member of multiple communities 

946 with communities_session(token1) as api: 

947 res = api.ListAllCommunities(communities_pb2.ListAllCommunitiesReq()) 

948 

949 # Should return all 10 communities 

950 assert len(res.communities) == 10 

951 

952 # Get all community IDs 

953 community_ids = [c.community_id for c in res.communities] 

954 assert set(community_ids) == { 

955 w_id, 

956 c1_id, 

957 c1r1_id, 

958 c1r1c1_id, 

959 c1r1c2_id, 

960 c1r2_id, 

961 c1r2c1_id, 

962 c2_id, 

963 c2r1_id, 

964 c2r1c1_id, 

965 } 

966 

967 # Check that each community has the required fields 

968 for community in res.communities: 

969 assert community.community_id > 0 

970 assert len(community.name) > 0 

971 assert len(community.slug) > 0 

972 assert community.member_count > 0 

973 # member field should be a boolean 

974 assert isinstance(community.member, bool) 

975 # parents should be present for hierarchical ordering 

976 assert len(community.parents) >= 1 

977 # created timestamp should be present 

978 assert community.HasField("created") 

979 assert community.created.seconds > 0 

980 

981 # Find specific communities and verify their data 

982 global_community = next(c for c in res.communities if c.community_id == w_id) 

983 assert global_community.name == "Global" 

984 assert global_community.slug == "global" 

985 assert global_community.member # user1 is a member 

986 assert global_community.member_count == 8 

987 assert len(global_community.parents) == 1 # Only itself 

988 

989 c1r1c1_community = next(c for c in res.communities if c.community_id == c1r1c1_id) 

990 assert c1r1c1_community.name == "Country 1, Region 1, City 1" 

991 assert c1r1c1_community.slug == "country-1-region-1-city-1" 

992 assert c1r1c1_community.member # user1 is a member 

993 assert c1r1c1_community.member_count == 3 

994 assert len(c1r1c1_community.parents) == 4 # Global, Country 1, Region 1, City 1 

995 # Verify parent hierarchy 

996 assert c1r1c1_community.parents[0].community.community_id == w_id 

997 assert c1r1c1_community.parents[1].community.community_id == c1_id 

998 assert c1r1c1_community.parents[2].community.community_id == c1r1_id 

999 assert c1r1c1_community.parents[3].community.community_id == c1r1c1_id 

1000 

1001 # Test with user6 who has different community memberships 

1002 with communities_session(token6) as api: 

1003 res = api.ListAllCommunities(communities_pb2.ListAllCommunitiesReq()) 

1004 

1005 # Should still return all 10 communities 

1006 assert len(res.communities) == 10 

1007 

1008 # Find Country 2 community - user6 should be a member 

1009 c2_community = next(c for c in res.communities if c.community_id == c2_id) 

1010 assert c2_community.member # user6 is a member 

1011 assert c2_community.member_count == 2 

1012 

1013 # Find Country 1 - user6 should NOT be a member 

1014 c1_community = next(c for c in res.communities if c.community_id == c1_id) 

1015 assert not c1_community.member # user6 is not a member 

1016 

1017 # Global - user6 should be a member 

1018 global_community = next(c for c in res.communities if c.community_id == w_id) 

1019 assert global_community.member # user6 is a member 

1020 

1021 

1022def test_JoinCommunity_and_LeaveCommunity(testing_communities): 

1023 # these are separate as they mutate the database 

1024 with session_scope() as session: 

1025 # at x=1, inside c1 (country 1) 

1026 user1_id, token1 = get_user_id_and_token(session, "user1") 

1027 # at x=51, not inside c1 

1028 user8_id, token8 = get_user_id_and_token(session, "user8") 

1029 c1_id = get_community_id(session, "Country 1") 

1030 

1031 with communities_session(token1) as api: 

1032 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1033 

1034 # user1 is already part of c1, cannot join 

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

1036 res = api.JoinCommunity( 

1037 communities_pb2.JoinCommunityReq( 

1038 community_id=c1_id, 

1039 ) 

1040 ) 

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

1042 assert e.value.details() == "You're already in that community." 

1043 

1044 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1045 

1046 # user1 is inside c1, cannot leave 

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

1048 res = api.LeaveCommunity( 

1049 communities_pb2.LeaveCommunityReq( 

1050 community_id=c1_id, 

1051 ) 

1052 ) 

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

1054 assert ( 

1055 e.value.details() 

1056 == "Your location on your profile is within this community, so you cannot leave it. However, you can adjust your notifications in your account settings." 

1057 ) 

1058 

1059 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1060 

1061 with communities_session(token8) as api: 

1062 assert not api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1063 

1064 # user8 is not in c1 yet, cannot leave 

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

1066 res = api.LeaveCommunity( 

1067 communities_pb2.LeaveCommunityReq( 

1068 community_id=c1_id, 

1069 ) 

1070 ) 

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

1072 assert e.value.details() == "You're not in that community." 

1073 

1074 assert not api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1075 

1076 # user8 is not in c1 and not part, can join 

1077 res = api.JoinCommunity( 

1078 communities_pb2.JoinCommunityReq( 

1079 community_id=c1_id, 

1080 ) 

1081 ) 

1082 

1083 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1084 

1085 # user8 is not in c1 and but now part, can't join again 

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

1087 res = api.JoinCommunity( 

1088 communities_pb2.JoinCommunityReq( 

1089 community_id=c1_id, 

1090 ) 

1091 ) 

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

1093 assert e.value.details() == "You're already in that community." 

1094 

1095 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1096 

1097 # user8 is not in c1 yet, but part of it, can leave 

1098 res = api.LeaveCommunity( 

1099 communities_pb2.LeaveCommunityReq( 

1100 community_id=c1_id, 

1101 ) 

1102 ) 

1103 assert not api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1104 

1105 

1106def test_LeaveCommunity_regression(db): 

1107 # See github issue #1444, repro: 

1108 # 1. Join more than one community 

1109 # 2. Leave one of them 

1110 # 3. You are no longer in any community 

1111 # admin 

1112 user1, token1 = generate_user(username="user1", geom=create_1d_point(200), geom_radius=0.1) 

1113 # joiner/leaver 

1114 user2, token2 = generate_user(username="user2", geom=create_1d_point(201), geom_radius=0.1) 

1115 

1116 with session_scope() as session: 

1117 c0 = create_community(session, 0, 100, "Community 0", [user1], [], None) 

1118 c1 = create_community(session, 0, 50, "Community 1", [user1], [], c0) 

1119 c2 = create_community(session, 0, 10, "Community 2", [user1], [], c0) 

1120 c0_id = c0.id 

1121 c1_id = c1.id 

1122 c2_id = c2.id 

1123 

1124 enforce_community_memberships() 

1125 

1126 with communities_session(token1) as api: 

1127 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c0_id)).member 

1128 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1129 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c2_id)).member 

1130 

1131 with communities_session(token2) as api: 

1132 # first check we're not in any communities 

1133 assert not api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c0_id)).member 

1134 assert not api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1135 assert not api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c2_id)).member 

1136 

1137 # join some communities 

1138 api.JoinCommunity(communities_pb2.JoinCommunityReq(community_id=c1_id)) 

1139 api.JoinCommunity(communities_pb2.JoinCommunityReq(community_id=c2_id)) 

1140 

1141 # check memberships 

1142 assert not api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c0_id)).member 

1143 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1144 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c2_id)).member 

1145 

1146 # leave just c2 

1147 api.LeaveCommunity(communities_pb2.LeaveCommunityReq(community_id=c2_id)) 

1148 

1149 # check memberships 

1150 assert not api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c0_id)).member 

1151 assert api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c1_id)).member 

1152 assert not api.GetCommunity(communities_pb2.GetCommunityReq(community_id=c2_id)).member 

1153 

1154 

1155def test_enforce_community_memberships_for_user(testing_communities): 

1156 """ 

1157 Make sure the user is added to the right communities on signup 

1158 """ 

1159 with auth_api_session() as (auth_api, metadata_interceptor): 

1160 res = auth_api.SignupFlow( 

1161 auth_pb2.SignupFlowReq( 

1162 basic=auth_pb2.SignupBasic(name="testing", email="email@couchers.org.invalid"), 

1163 account=auth_pb2.SignupAccount( 

1164 username="frodo", 

1165 password="a very insecure password", 

1166 birthdate="1970-01-01", 

1167 gender="Bot", 

1168 hosting_status=api_pb2.HOSTING_STATUS_CAN_HOST, 

1169 city="Country 1, Region 1, City 2", 

1170 # lat=8, lng=1 is equivalent to creating this coordinate with create_coordinate(8) 

1171 lat=8, 

1172 lng=1, 

1173 radius=500, 

1174 accept_tos=True, 

1175 ), 

1176 feedback=auth_pb2.ContributorForm(), 

1177 accept_community_guidelines=wrappers_pb2.BoolValue(value=True), 

1178 ) 

1179 ) 

1180 with session_scope() as session: 

1181 email_token = ( 

1182 session.execute(select(SignupFlow).where(SignupFlow.flow_token == res.flow_token)).scalar_one().email_token 

1183 ) 

1184 with auth_api_session() as (auth_api, metadata_interceptor): 

1185 res = auth_api.SignupFlow(auth_pb2.SignupFlowReq(email_token=email_token)) 

1186 user_id = res.auth_res.user_id 

1187 

1188 # now check the user is in the right communities 

1189 with session_scope() as session: 

1190 w_id = get_community_id(session, "Global") 

1191 c1_id = get_community_id(session, "Country 1") 

1192 c1r1_id = get_community_id(session, "Country 1, Region 1") 

1193 c1r1c2_id = get_community_id(session, "Country 1, Region 1, City 2") 

1194 

1195 token, _ = get_session_cookie_tokens(metadata_interceptor) 

1196 

1197 with communities_session(token) as api: 

1198 res = api.ListUserCommunities(communities_pb2.ListUserCommunitiesReq()) 

1199 assert [c.community_id for c in res.communities] == [w_id, c1_id, c1r1_id, c1r1c2_id] 

1200 

1201 

1202# TODO: requires transferring of content 

1203 

1204# def test_ListPlaces(db, testing_communities): 

1205# pass 

1206 

1207# def test_ListGuides(db, testing_communities): 

1208# pass 

1209 

1210# def test_ListEvents(db, testing_communities): 

1211# pass