Coverage for app/backend/src/tests/test_blocking.py: 100%

186 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-09-19 15:47 +0000

1import grpc 

2import pytest 

3from google.protobuf import empty_pb2 

4from sqlalchemy import select 

5 

6from couchers.db import session_scope 

7from couchers.models import User, UserBlock 

8from couchers.proto import blocking_pb2 

9from couchers.servicers.blocking import is_not_visible 

10from couchers.utils import now 

11from tests.fixtures.db import generate_user, make_user_block 

12from tests.fixtures.sessions import blocking_session 

13 

14 

15def test_BlockUser(db): 

16 user1, token1 = generate_user() 

17 user2, token2 = generate_user() 

18 

19 with session_scope() as session: 

20 blocked_user_list = ( 

21 session.execute(select(UserBlock).where(UserBlock.blocking_user_id == user1.id)).scalars().all() 

22 ) 

23 assert len(blocked_user_list) == 0 

24 

25 with blocking_session(token1) as user_blocks: 

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

27 user_blocks.BlockUser(blocking_pb2.BlockUserReq(username=user1.username)) 

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

29 assert e.value.details() == "You can't block yourself." 

30 

31 user_blocks.BlockUser(blocking_pb2.BlockUserReq(username=user2.username)) 

32 

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

34 user_blocks.BlockUser(blocking_pb2.BlockUserReq(username=user2.username)) 

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

36 assert e.value.details() == "Target user has already been blocked." 

37 

38 with session_scope() as session: 

39 blocked_user_list = ( 

40 session.execute(select(UserBlock).where(UserBlock.blocking_user_id == user1.id)).scalars().all() 

41 ) 

42 assert len(blocked_user_list) == 1 

43 

44 

45def test_make_user_block(db): 

46 user1, token1 = generate_user() 

47 user2, token2 = generate_user() 

48 

49 make_user_block(user1, user2) 

50 

51 with session_scope() as session: 

52 blocked_user_list = ( 

53 session.execute(select(UserBlock).where(UserBlock.blocking_user_id == user1.id)).scalars().all() 

54 ) 

55 assert len(blocked_user_list) == 1 

56 

57 

58def test_UnblockUser(db): 

59 user1, token1 = generate_user() 

60 user2, token2 = generate_user() 

61 make_user_block(user1, user2) 

62 

63 with blocking_session(token1) as user_blocks: 

64 user_blocks.UnblockUser(blocking_pb2.UnblockUserReq(username=user2.username)) 

65 

66 with session_scope() as session: 

67 blocked_users = session.execute(select(UserBlock).where(UserBlock.blocking_user_id == user1.id)).scalars().all() 

68 assert len(blocked_users) == 0 

69 

70 with blocking_session(token1) as user_blocks: 

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

72 user_blocks.UnblockUser(blocking_pb2.UnblockUserReq(username=user2.username)) 

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

74 assert e.value.details() == "Target user is not blocked." 

75 

76 # Test re-blocking 

77 user_blocks.BlockUser(blocking_pb2.BlockUserReq(username=user2.username)) 

78 

79 with session_scope() as session: 

80 blocked_users = session.execute(select(UserBlock).where(UserBlock.blocking_user_id == user1.id)).scalars().all() 

81 assert len(blocked_users) == 1 

82 

83 

84def test_GetBlockedUsers(db): 

85 user1, token1 = generate_user() 

86 user2, token2 = generate_user() 

87 user3, token3 = generate_user() 

88 

89 with blocking_session(token1) as user_blocks: 

90 # Check no blocked users to start 

91 blocked_user_list = user_blocks.GetBlockedUsers(empty_pb2.Empty()) 

92 assert len(blocked_user_list.blocked_users) == 0 

93 

94 make_user_block(user1, user2) 

95 make_user_block(user1, user3) 

96 blocked_user_list = user_blocks.GetBlockedUsers(empty_pb2.Empty()) 

97 assert len(blocked_user_list.blocked_users) == 2 

98 

99 blocked_usernames = [user.username for user in blocked_user_list.blocked_users] 

100 blocked_names = [user.name for user in blocked_user_list.blocked_users] 

101 

102 assert user2.username in blocked_usernames 

103 assert user3.username in blocked_usernames 

104 assert user2.name in blocked_names 

105 assert user3.name in blocked_names 

106 

107 

108def test_relationships_userblock_dot_user(db): 

109 user1, token1 = generate_user() 

110 user2, token2 = generate_user() 

111 

112 make_user_block(user1, user2) 

113 

114 with session_scope() as session: 

115 block = session.execute( 

116 select(UserBlock).where((UserBlock.blocking_user_id == user1.id) & (UserBlock.blocked_user_id == user2.id)) 

117 ).scalar_one() 

118 

119 blocking_user_username = block.blocking_user.username 

120 blocking_user_name = block.blocking_user.name 

121 

122 blocked_user_username = block.blocked_user.username 

123 blocked_user_name = block.blocked_user.name 

124 

125 assert blocking_user_username == user1.username 

126 assert blocked_user_username == user2.username 

127 assert blocking_user_name == user1.name 

128 assert blocked_user_name == user2.name 

129 

130 

131def test_is_not_visible(db): 

132 """ 

133 Comprehensive tests for is_not_visible function covering: 

134 1. Normal visible users (not blocked, not banned, not deleted) - should be visible to each other 

135 2. User1 blocks User2 - not visible 

136 3. User2 blocks User1 - not visible 

137 4. Mutual blocking - not visible 

138 5. User1 is deleted - not visible 

139 6. User2 is deleted - not visible 

140 7. Both users deleted - not visible 

141 8. User1 is banned - not visible 

142 9. User2 is banned - not visible 

143 10. Both users banned - not visible 

144 11. Mixed: User1 deleted and User2 banned - not visible 

145 12. None user_id cases with visible users 

146 13. None user_id cases with hidden users (banned/deleted) 

147 14. Ensure we have extra users in DB to avoid edge cases with empty database 

148 """ 

149 

150 # Create extra users to ensure the database is not empty - these should remain visible 

151 extra_user1, _ = generate_user() 

152 extra_user2, _ = generate_user() 

153 extra_user3, _ = generate_user() 

154 

155 # Create users for testing - all start as visible 

156 normal_user1, _ = generate_user() 

157 normal_user2, _ = generate_user() 

158 

159 # Users for blocking tests 

160 blocker_user, _ = generate_user() 

161 blockee_user, _ = generate_user() 

162 

163 # Users for reverse blocking tests 

164 reverse_blocker, _ = generate_user() 

165 reverse_blockee, _ = generate_user() 

166 

167 # Users for mutual blocking tests 

168 mutual_blocker1, _ = generate_user() 

169 mutual_blocker2, _ = generate_user() 

170 

171 # Users for deletion tests 

172 deleted_user1, _ = generate_user() 

173 visible_for_deleted, _ = generate_user() 

174 deleted_user2, _ = generate_user() 

175 both_deleted1, _ = generate_user() 

176 both_deleted2, _ = generate_user() 

177 

178 # Users for ban tests 

179 banned_user1, _ = generate_user() 

180 visible_for_banned, _ = generate_user() 

181 banned_user2, _ = generate_user() 

182 both_banned1, _ = generate_user() 

183 both_banned2, _ = generate_user() 

184 

185 # User for mixed test 

186 mixed_deleted, _ = generate_user() 

187 mixed_banned, _ = generate_user() 

188 

189 # Users for shadow tests 

190 shadowed_user, _ = generate_user() 

191 visible_for_shadowed, _ = generate_user() 

192 

193 with session_scope() as session: 

194 # Test 1: Two normal visible users - should be visible to each other 

195 assert not is_not_visible(session, normal_user1.id, normal_user2.id) 

196 assert not is_not_visible(session, normal_user2.id, normal_user1.id) 

197 

198 # Test 2: User1 blocks User2 - should not be visible 

199 make_user_block(blocker_user, blockee_user) 

200 assert is_not_visible(session, blocker_user.id, blockee_user.id) 

201 assert is_not_visible(session, blockee_user.id, blocker_user.id) # symmetric 

202 

203 # Test 3: User2 blocks User1 (reverse block) - should not be visible 

204 make_user_block(reverse_blockee, reverse_blocker) 

205 assert is_not_visible(session, reverse_blocker.id, reverse_blockee.id) 

206 assert is_not_visible(session, reverse_blockee.id, reverse_blocker.id) # symmetric 

207 

208 # Test 4: Mutual blocking - should not be visible 

209 make_user_block(mutual_blocker1, mutual_blocker2) 

210 make_user_block(mutual_blocker2, mutual_blocker1) 

211 assert is_not_visible(session, mutual_blocker1.id, mutual_blocker2.id) 

212 assert is_not_visible(session, mutual_blocker2.id, mutual_blocker1.id) 

213 

214 # Test 5: User1 is deleted - should not be visible 

215 deleted_user1_db = session.get_one(User, deleted_user1.id) 

216 deleted_user1_db.deleted_at = now() 

217 session.commit() 

218 assert is_not_visible(session, deleted_user1.id, visible_for_deleted.id) 

219 assert is_not_visible(session, visible_for_deleted.id, deleted_user1.id) 

220 

221 # Test 6: User2 is deleted - should not be visible 

222 deleted_user2_db = session.get_one(User, deleted_user2.id) 

223 deleted_user2_db.deleted_at = now() 

224 session.commit() 

225 assert is_not_visible(session, normal_user1.id, deleted_user2.id) 

226 assert is_not_visible(session, deleted_user2.id, normal_user1.id) 

227 

228 # Test 7: Both users deleted - should not be visible 

229 both_deleted1_db = session.get_one(User, both_deleted1.id) 

230 both_deleted2_db = session.get_one(User, both_deleted2.id) 

231 both_deleted1_db.deleted_at = now() 

232 both_deleted2_db.deleted_at = now() 

233 session.commit() 

234 assert is_not_visible(session, both_deleted1.id, both_deleted2.id) 

235 assert is_not_visible(session, both_deleted2.id, both_deleted1.id) 

236 

237 # Test 8: User1 is banned - should not be visible 

238 banned_user1_db = session.get_one(User, banned_user1.id) 

239 banned_user1_db.banned_at = now() 

240 session.commit() 

241 assert is_not_visible(session, banned_user1.id, visible_for_banned.id) 

242 assert is_not_visible(session, visible_for_banned.id, banned_user1.id) 

243 

244 # Test 9: User2 is banned - should not be visible 

245 banned_user2_db = session.get_one(User, banned_user2.id) 

246 banned_user2_db.banned_at = now() 

247 session.commit() 

248 assert is_not_visible(session, normal_user2.id, banned_user2.id) 

249 assert is_not_visible(session, banned_user2.id, normal_user2.id) 

250 

251 # Test 10: Both users banned - should not be visible 

252 both_banned1_db = session.get_one(User, both_banned1.id) 

253 both_banned2_db = session.get_one(User, both_banned2.id) 

254 both_banned1_db.banned_at = now() 

255 both_banned2_db.banned_at = now() 

256 session.commit() 

257 assert is_not_visible(session, both_banned1.id, both_banned2.id) 

258 assert is_not_visible(session, both_banned2.id, both_banned1.id) 

259 

260 # Test 11: Mixed - one deleted, one banned - should not be visible 

261 mixed_deleted_db = session.get_one(User, mixed_deleted.id) 

262 mixed_banned_db = session.get_one(User, mixed_banned.id) 

263 mixed_deleted_db.deleted_at = now() 

264 mixed_banned_db.banned_at = now() 

265 session.commit() 

266 assert is_not_visible(session, mixed_deleted.id, mixed_banned.id) 

267 assert is_not_visible(session, mixed_banned.id, mixed_deleted.id) 

268 

269 # Test 12: None user_id cases with visible users - should be visible 

270 assert not is_not_visible(session, None, normal_user1.id) 

271 assert not is_not_visible(session, normal_user1.id, None) 

272 assert not is_not_visible(session, None, None) 

273 

274 # Test 13: None user_id cases with hidden users (deleted/banned) - should not be visible 

275 assert is_not_visible(session, None, deleted_user1.id) 

276 assert is_not_visible(session, deleted_user1.id, None) 

277 assert is_not_visible(session, None, banned_user1.id) 

278 assert is_not_visible(session, banned_user1.id, None) 

279 

280 # Test 14: Verify extra users are still visible (database not empty) 

281 assert not is_not_visible(session, extra_user1.id, extra_user2.id) 

282 assert not is_not_visible(session, extra_user2.id, extra_user3.id) 

283 assert not is_not_visible(session, extra_user1.id, extra_user3.id) 

284 

285 # Additional edge case: Check that normal users are still visible to each other after all the above 

286 assert not is_not_visible(session, normal_user1.id, normal_user2.id) 

287 assert not is_not_visible(session, normal_user1.id, extra_user1.id) 

288 

289 # Test 15: Shadowed target is hidden from other users and from anonymous viewers, 

290 # but visible to themselves 

291 shadowed_user_db = session.get_one(User, shadowed_user.id) 

292 shadowed_user_db.shadowed_at = now() 

293 session.commit() 

294 assert is_not_visible(session, visible_for_shadowed.id, shadowed_user.id) 

295 assert is_not_visible(session, None, shadowed_user.id) 

296 assert not is_not_visible(session, shadowed_user.id, shadowed_user.id) 

297 # Shadowed viewer can still see other (visible) users 

298 assert not is_not_visible(session, shadowed_user.id, visible_for_shadowed.id) 

299 assert not is_not_visible(session, shadowed_user.id, None)