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

254 statements  

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

1import grpc 

2import pytest 

3 

4from couchers.db import session_scope 

5from couchers.proto import groups_pb2, pages_pb2 

6from couchers.tasks import enforce_community_memberships 

7from tests.test_communities import ( # noqa 

8 create_1d_point, 

9 create_community, 

10 create_group, 

11 get_community_id, 

12 get_group_id, 

13 testing_communities, 

14) 

15from tests.test_fixtures import db, generate_user, get_user_id_and_token, groups_session, testconfig # noqa 

16 

17 

18@pytest.fixture(autouse=True) 

19def _(testconfig): 

20 pass 

21 

22 

23class TestGroups: 

24 @staticmethod 

25 def test_GetGroup(testing_communities): 

26 # implicitly tests visibility and blocking, since all groups have invisible, blocked, and blocking member and admin 

27 with session_scope() as session: 

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

29 w_id = get_community_id(session, "Global") 

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

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

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

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

34 hitchhikers_id = get_group_id(session, "Hitchhikers") 

35 c1r2foodies_id = get_group_id(session, "Country 1, Region 2, Foodies") 

36 c2r1foodies_id = get_group_id(session, "Country 2, Region 1, Foodies") 

37 

38 with groups_session(token2) as api: 

39 res = api.GetGroup( 

40 groups_pb2.GetGroupReq( 

41 group_id=hitchhikers_id, 

42 ) 

43 ) 

44 assert res.group_id == hitchhikers_id 

45 assert res.name == "Hitchhikers" 

46 assert res.slug == "hitchhikers" 

47 assert res.description == "Description for Hitchhikers" 

48 assert len(res.parents) == 2 

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

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

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

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

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

54 assert res.parents[1].HasField("group") 

55 assert res.parents[1].group.group_id == hitchhikers_id 

56 assert res.parents[1].group.name == "Hitchhikers" 

57 assert res.parents[1].group.slug == "hitchhikers" 

58 assert res.parents[1].group.description == "Description for Hitchhikers" 

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

60 assert res.main_page.slug == "main-page-for-the-hitchhikers-community" 

61 assert res.main_page.last_editor_user_id == 1 

62 assert res.main_page.creator_user_id == 1 

63 assert res.main_page.owner_group_id == hitchhikers_id 

64 assert res.main_page.title == "Main page for the Hitchhikers community" 

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

66 assert res.main_page.can_edit 

67 assert not res.main_page.can_moderate 

68 assert res.main_page.editor_user_ids == [1] 

69 assert res.member 

70 assert res.admin 

71 assert res.member_count == 4 

72 assert res.admin_count == 2 

73 

74 res = api.GetGroup( 

75 groups_pb2.GetGroupReq( 

76 group_id=c1r2foodies_id, 

77 ) 

78 ) 

79 assert res.group_id == c1r2foodies_id 

80 assert res.name == "Country 1, Region 2, Foodies" 

81 assert res.slug == "country-1-region-2-foodies" 

82 assert res.description == "Description for Country 1, Region 2, Foodies" 

83 assert len(res.parents) == 4 

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

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

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

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

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

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

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

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

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

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

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

95 assert res.parents[2].community.community_id == c1r2_id 

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

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

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

99 assert res.parents[3].HasField("group") 

100 assert res.parents[3].group.group_id == c1r2foodies_id 

101 assert res.parents[3].group.name == "Country 1, Region 2, Foodies" 

102 assert res.parents[3].group.slug == "country-1-region-2-foodies" 

103 assert res.parents[3].group.description == "Description for Country 1, Region 2, Foodies" 

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

105 assert res.main_page.slug == "main-page-for-the-country-1-region-2-foodies-community" 

106 assert res.main_page.last_editor_user_id == 2 

107 assert res.main_page.creator_user_id == 2 

108 assert res.main_page.owner_group_id == c1r2foodies_id 

109 assert res.main_page.title == "Main page for the Country 1, Region 2, Foodies community" 

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

111 assert res.main_page.can_edit 

112 assert res.main_page.can_moderate 

113 assert res.main_page.editor_user_ids == [2] 

114 assert res.member 

115 assert res.admin 

116 assert res.member_count == 3 

117 assert res.admin_count == 1 

118 

119 res = api.GetGroup( 

120 groups_pb2.GetGroupReq( 

121 group_id=c2r1foodies_id, 

122 ) 

123 ) 

124 assert res.group_id == c2r1foodies_id 

125 assert res.name == "Country 2, Region 1, Foodies" 

126 assert res.slug == "country-2-region-1-foodies" 

127 assert res.description == "Description for Country 2, Region 1, Foodies" 

128 assert len(res.parents) == 4 

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

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

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

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

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

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

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

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

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

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

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

140 assert res.parents[2].community.community_id == c2r1_id 

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

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

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

144 assert res.parents[3].HasField("group") 

145 assert res.parents[3].group.group_id == c2r1foodies_id 

146 assert res.parents[3].group.name == "Country 2, Region 1, Foodies" 

147 assert res.parents[3].group.slug == "country-2-region-1-foodies" 

148 assert res.parents[3].group.description == "Description for Country 2, Region 1, Foodies" 

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

150 assert res.main_page.slug == "main-page-for-the-country-2-region-1-foodies-community" 

151 assert res.main_page.last_editor_user_id == 6 

152 assert res.main_page.creator_user_id == 6 

153 assert res.main_page.owner_group_id == c2r1foodies_id 

154 assert res.main_page.title == "Main page for the Country 2, Region 1, Foodies community" 

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

156 assert not res.main_page.can_edit 

157 assert not res.main_page.can_moderate 

158 assert res.main_page.editor_user_ids == [6] 

159 assert not res.member 

160 assert not res.admin 

161 assert res.member_count == 2 

162 assert res.admin_count == 1 

163 

164 @staticmethod 

165 def test_ListAdmins(testing_communities): 

166 # implicitly tests visibility and blocking, since all groups have invisible, blocked, and blocking admin 

167 with session_scope() as session: 

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

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

170 hitchhikers_id = get_group_id(session, "Hitchhikers") 

171 c1r2foodies_id = get_group_id(session, "Country 1, Region 2, Foodies") 

172 

173 with groups_session(token1) as api: 

174 res = api.ListAdmins( 

175 groups_pb2.ListAdminsReq( 

176 group_id=hitchhikers_id, 

177 ) 

178 ) 

179 assert res.admin_user_ids == [user1_id, user2_id] 

180 

181 res = api.ListAdmins( 

182 groups_pb2.ListAdminsReq( 

183 group_id=c1r2foodies_id, 

184 ) 

185 ) 

186 assert res.admin_user_ids == [user2_id] 

187 

188 @staticmethod 

189 def test_ListMembers(testing_communities): 

190 # implicitly tests visibility and blocking, since all groups have invisible, blocked, and blocking member 

191 with session_scope() as session: 

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

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

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

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

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

197 hitchhikers_id = get_group_id(session, "Hitchhikers") 

198 c1r2foodies_id = get_group_id(session, "Country 1, Region 2, Foodies") 

199 

200 with groups_session(token1) as api: 

201 res = api.ListMembers( 

202 groups_pb2.ListMembersReq( 

203 group_id=hitchhikers_id, 

204 ) 

205 ) 

206 assert res.member_user_ids == [user1_id, user2_id, user5_id, user8_id] 

207 

208 res = api.ListMembers( 

209 groups_pb2.ListMembersReq( 

210 group_id=c1r2foodies_id, 

211 ) 

212 ) 

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

214 

215 @staticmethod 

216 def test_ListDiscussions(testing_communities): 

217 with session_scope() as session: 

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

219 hitchhikers_id = get_group_id(session, "Hitchhikers") 

220 

221 with groups_session(token1) as api: 

222 res = api.ListDiscussions( 

223 groups_pb2.ListDiscussionsReq( 

224 group_id=hitchhikers_id, 

225 page_size=5, 

226 ) 

227 ) 

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

229 "Discussion title 8", 

230 "Discussion title 9", 

231 "Discussion title 10", 

232 "Discussion title 11", 

233 "Discussion title 12", 

234 ] 

235 for d in res.discussions: 

236 assert d.thread.thread_id > 0 

237 assert d.thread.num_responses == 0 

238 

239 res = api.ListDiscussions( 

240 groups_pb2.ListDiscussionsReq( 

241 group_id=hitchhikers_id, 

242 page_token=res.next_page_token, 

243 page_size=5, 

244 ) 

245 ) 

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

247 "Discussion title 13", 

248 "Discussion title 14", 

249 ] 

250 for d in res.discussions: 

251 assert d.thread.thread_id > 0 

252 assert d.thread.num_responses == 0 

253 

254 @staticmethod 

255 def test_ListUserGroups(testing_communities): 

256 with session_scope() as session: 

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

258 hitchhikers_id = get_group_id(session, "Hitchhikers") 

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

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

261 

262 # List user1's groups from user1's account 

263 with groups_session(token1) as api: 

264 res = api.ListUserGroups(groups_pb2.ListUserGroupsReq()) 

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

266 

267 @staticmethod 

268 def test_ListOtherUserGroups(testing_communities): 

269 with session_scope() as session: 

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

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

272 hitchhikers_id = get_group_id(session, "Hitchhikers") 

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

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

275 

276 # List user1's groups from user2's account 

277 with groups_session(token2) as api: 

278 res = api.ListUserGroups(groups_pb2.ListUserGroupsReq(user_id=user1_id)) 

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

280 

281 @staticmethod 

282 def test_ListEvents(testing_communities): 

283 with session_scope() as session: 

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

285 hitchhikers_id = get_group_id(session, "Hitchhikers") 

286 

287 with groups_session(token7) as api: 

288 res = api.ListEvents( 

289 groups_pb2.ListEventsReq( 

290 group_id=hitchhikers_id, 

291 page_size=3, 

292 ) 

293 ) 

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

295 "Event title 7", 

296 "Event title 8", 

297 "Event title 9", 

298 ] 

299 

300 res = api.ListEvents( 

301 groups_pb2.ListEventsReq( 

302 group_id=hitchhikers_id, 

303 page_token=res.next_page_token, 

304 page_size=2, 

305 ) 

306 ) 

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

308 "Event title 10", 

309 "Event title 11", 

310 ] 

311 

312 res = api.ListEvents( 

313 groups_pb2.ListEventsReq( 

314 group_id=hitchhikers_id, 

315 page_token=res.next_page_token, 

316 page_size=2, 

317 ) 

318 ) 

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

320 "Event title 12", 

321 ] 

322 assert not res.next_page_token 

323 

324 

325def test_JoinGroup_and_LeaveGroup(testing_communities): 

326 # these tests are separate from above as they mutate the database 

327 with session_scope() as session: 

328 user_id, token = get_user_id_and_token(session, "user3") 

329 h_id = get_group_id(session, "Hitchhikers") 

330 

331 with groups_session(token) as api: 

332 # not in group at start 

333 assert not api.GetGroup(groups_pb2.GetGroupReq(group_id=h_id)).member 

334 

335 # can't leave 

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

337 res = api.LeaveGroup( 

338 groups_pb2.LeaveGroupReq( 

339 group_id=h_id, 

340 ) 

341 ) 

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

343 assert e.value.details() == "You're not in that group." 

344 

345 # didn't magically join 

346 assert not api.GetGroup(groups_pb2.GetGroupReq(group_id=h_id)).member 

347 

348 # but can join 

349 res = api.JoinGroup( 

350 groups_pb2.JoinGroupReq( 

351 group_id=h_id, 

352 ) 

353 ) 

354 

355 # should be there now 

356 assert api.GetGroup(groups_pb2.GetGroupReq(group_id=h_id)).member 

357 

358 # can't join again 

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

360 res = api.JoinGroup( 

361 groups_pb2.JoinGroupReq( 

362 group_id=h_id, 

363 ) 

364 ) 

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

366 assert e.value.details() == "You're already in that group." 

367 

368 # didn't magically leave 

369 assert api.GetGroup(groups_pb2.GetGroupReq(group_id=h_id)).member 

370 

371 # now we can leave though 

372 res = api.LeaveGroup( 

373 groups_pb2.LeaveGroupReq( 

374 group_id=h_id, 

375 ) 

376 ) 

377 

378 # managed to leave 

379 assert not api.GetGroup(groups_pb2.GetGroupReq(group_id=h_id)).member 

380 

381 

382def test_LeaveGroup_regression(db): 

383 # see test_LeaveCommunity_regression 

384 

385 # admin 

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

387 # joiner/leaver 

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

389 

390 with session_scope() as session: 

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

392 g1 = create_group(session, "Group 1", [user1], [], c0) 

393 g2 = create_group(session, "Group 2", [user1], [], c0) 

394 g1_id = g1.id 

395 g2_id = g2.id 

396 

397 enforce_community_memberships() 

398 

399 with groups_session(token1) as api: 

400 assert api.GetGroup(groups_pb2.GetGroupReq(group_id=g1_id)).member 

401 assert api.GetGroup(groups_pb2.GetGroupReq(group_id=g2_id)).member 

402 

403 with groups_session(token2) as api: 

404 # first check we're not in any groups 

405 assert not api.GetGroup(groups_pb2.GetGroupReq(group_id=g1_id)).member 

406 assert not api.GetGroup(groups_pb2.GetGroupReq(group_id=g2_id)).member 

407 

408 # join some groups 

409 api.JoinGroup(groups_pb2.JoinGroupReq(group_id=g1_id)) 

410 api.JoinGroup(groups_pb2.JoinGroupReq(group_id=g2_id)) 

411 

412 # check memberships 

413 assert api.GetGroup(groups_pb2.GetGroupReq(group_id=g1_id)).member 

414 assert api.GetGroup(groups_pb2.GetGroupReq(group_id=g2_id)).member 

415 

416 # leave just g2 

417 api.LeaveGroup(groups_pb2.LeaveGroupReq(group_id=g2_id)) 

418 

419 # check memberships 

420 assert api.GetGroup(groups_pb2.GetGroupReq(group_id=g1_id)).member 

421 assert not api.GetGroup(groups_pb2.GetGroupReq(group_id=g2_id)).member 

422 

423 

424# TODO: also requires implementing content transfer functionality 

425# Note: allegedly groups cannot contain content other than discussions! 

426 

427# def test_ListPlaces(db, testing_communities): 

428# pass 

429 

430# def test_ListGuides(db, testing_communities): 

431# pass 

432 

433# def test_ListEvents(db, testing_communities): 

434# pass