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

189 statements  

« prev     ^ index     » next       coverage.py v7.14.2, created at 2026-06-21 09:29 +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 

15@pytest.fixture(autouse=True) 

16def _(testconfig): 

17 pass 

18 

19 

20def test_BlockUser(db): 

21 user1, token1 = generate_user() 

22 user2, token2 = generate_user() 

23 

24 with session_scope() as session: 

25 blocked_user_list = ( 

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

27 ) 

28 assert len(blocked_user_list) == 0 

29 

30 with blocking_session(token1) as user_blocks: 

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

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

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

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

35 

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

37 

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

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

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

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

42 

43 with session_scope() as session: 

44 blocked_user_list = ( 

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

46 ) 

47 assert len(blocked_user_list) == 1 

48 

49 

50def test_make_user_block(db): 

51 user1, token1 = generate_user() 

52 user2, token2 = generate_user() 

53 

54 make_user_block(user1, user2) 

55 

56 with session_scope() as session: 

57 blocked_user_list = ( 

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

59 ) 

60 assert len(blocked_user_list) == 1 

61 

62 

63def test_UnblockUser(db): 

64 user1, token1 = generate_user() 

65 user2, token2 = generate_user() 

66 make_user_block(user1, user2) 

67 

68 with blocking_session(token1) as user_blocks: 

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

70 

71 with session_scope() as session: 

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

73 assert len(blocked_users) == 0 

74 

75 with blocking_session(token1) as user_blocks: 

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

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

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

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

80 

81 # Test re-blocking 

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

83 

84 with session_scope() as session: 

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

86 assert len(blocked_users) == 1 

87 

88 

89def test_GetBlockedUsers(db): 

90 user1, token1 = generate_user() 

91 user2, token2 = generate_user() 

92 user3, token3 = generate_user() 

93 

94 with blocking_session(token1) as user_blocks: 

95 # Check no blocked users to start 

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

97 assert len(blocked_user_list.blocked_users) == 0 

98 

99 make_user_block(user1, user2) 

100 make_user_block(user1, user3) 

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

102 assert len(blocked_user_list.blocked_users) == 2 

103 

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

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

106 

107 assert user2.username in blocked_usernames 

108 assert user3.username in blocked_usernames 

109 assert user2.name in blocked_names 

110 assert user3.name in blocked_names 

111 

112 

113def test_relationships_userblock_dot_user(db): 

114 user1, token1 = generate_user() 

115 user2, token2 = generate_user() 

116 

117 make_user_block(user1, user2) 

118 

119 with session_scope() as session: 

120 block = session.execute( 

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

122 ).scalar_one() 

123 

124 blocking_user_username = block.blocking_user.username 

125 blocking_user_name = block.blocking_user.name 

126 

127 blocked_user_username = block.blocked_user.username 

128 blocked_user_name = block.blocked_user.name 

129 

130 assert blocking_user_username == user1.username 

131 assert blocked_user_username == user2.username 

132 assert blocking_user_name == user1.name 

133 assert blocked_user_name == user2.name 

134 

135 

136def test_is_not_visible(db): 

137 """ 

138 Comprehensive tests for is_not_visible function covering: 

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

140 2. User1 blocks User2 - not visible 

141 3. User2 blocks User1 - not visible 

142 4. Mutual blocking - not visible 

143 5. User1 is deleted - not visible 

144 6. User2 is deleted - not visible 

145 7. Both users deleted - not visible 

146 8. User1 is banned - not visible 

147 9. User2 is banned - not visible 

148 10. Both users banned - not visible 

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

150 12. None user_id cases with visible users 

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

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

153 """ 

154 

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

156 extra_user1, _ = generate_user() 

157 extra_user2, _ = generate_user() 

158 extra_user3, _ = generate_user() 

159 

160 # Create users for testing - all start as visible 

161 normal_user1, _ = generate_user() 

162 normal_user2, _ = generate_user() 

163 

164 # Users for blocking tests 

165 blocker_user, _ = generate_user() 

166 blockee_user, _ = generate_user() 

167 

168 # Users for reverse blocking tests 

169 reverse_blocker, _ = generate_user() 

170 reverse_blockee, _ = generate_user() 

171 

172 # Users for mutual blocking tests 

173 mutual_blocker1, _ = generate_user() 

174 mutual_blocker2, _ = generate_user() 

175 

176 # Users for deletion tests 

177 deleted_user1, _ = generate_user() 

178 visible_for_deleted, _ = generate_user() 

179 deleted_user2, _ = generate_user() 

180 both_deleted1, _ = generate_user() 

181 both_deleted2, _ = generate_user() 

182 

183 # Users for ban tests 

184 banned_user1, _ = generate_user() 

185 visible_for_banned, _ = generate_user() 

186 banned_user2, _ = generate_user() 

187 both_banned1, _ = generate_user() 

188 both_banned2, _ = generate_user() 

189 

190 # User for mixed test 

191 mixed_deleted, _ = generate_user() 

192 mixed_banned, _ = generate_user() 

193 

194 # Users for shadow tests 

195 shadowed_user, _ = generate_user() 

196 visible_for_shadowed, _ = generate_user() 

197 

198 with session_scope() as session: 

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

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

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

202 

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

204 make_user_block(blocker_user, blockee_user) 

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

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

207 

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

209 make_user_block(reverse_blockee, reverse_blocker) 

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

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

212 

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

214 make_user_block(mutual_blocker1, mutual_blocker2) 

215 make_user_block(mutual_blocker2, mutual_blocker1) 

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

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

218 

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

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

221 deleted_user1_db.deleted_at = now() 

222 session.commit() 

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

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

225 

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

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

228 deleted_user2_db.deleted_at = now() 

229 session.commit() 

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

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

232 

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

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

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

236 both_deleted1_db.deleted_at = now() 

237 both_deleted2_db.deleted_at = now() 

238 session.commit() 

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

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

241 

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

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

244 banned_user1_db.banned_at = now() 

245 session.commit() 

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

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

248 

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

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

251 banned_user2_db.banned_at = now() 

252 session.commit() 

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

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

255 

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

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

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

259 both_banned1_db.banned_at = now() 

260 both_banned2_db.banned_at = now() 

261 session.commit() 

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

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

264 

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

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

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

268 mixed_deleted_db.deleted_at = now() 

269 mixed_banned_db.banned_at = now() 

270 session.commit() 

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

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

273 

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

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

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

277 assert not is_not_visible(session, None, None) 

278 

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

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

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

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

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

284 

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

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

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

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

289 

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

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

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

293 

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

295 # but visible to themselves 

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

297 shadowed_user_db.shadowed_at = now() 

298 session.commit() 

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

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

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

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

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

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