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
« 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
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
15def test_BlockUser(db):
16 user1, token1 = generate_user()
17 user2, token2 = generate_user()
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
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."
31 user_blocks.BlockUser(blocking_pb2.BlockUserReq(username=user2.username))
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."
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
45def test_make_user_block(db):
46 user1, token1 = generate_user()
47 user2, token2 = generate_user()
49 make_user_block(user1, user2)
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
58def test_UnblockUser(db):
59 user1, token1 = generate_user()
60 user2, token2 = generate_user()
61 make_user_block(user1, user2)
63 with blocking_session(token1) as user_blocks:
64 user_blocks.UnblockUser(blocking_pb2.UnblockUserReq(username=user2.username))
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
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."
76 # Test re-blocking
77 user_blocks.BlockUser(blocking_pb2.BlockUserReq(username=user2.username))
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
84def test_GetBlockedUsers(db):
85 user1, token1 = generate_user()
86 user2, token2 = generate_user()
87 user3, token3 = generate_user()
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
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
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]
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
108def test_relationships_userblock_dot_user(db):
109 user1, token1 = generate_user()
110 user2, token2 = generate_user()
112 make_user_block(user1, user2)
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()
119 blocking_user_username = block.blocking_user.username
120 blocking_user_name = block.blocking_user.name
122 blocked_user_username = block.blocked_user.username
123 blocked_user_name = block.blocked_user.name
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
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 """
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()
155 # Create users for testing - all start as visible
156 normal_user1, _ = generate_user()
157 normal_user2, _ = generate_user()
159 # Users for blocking tests
160 blocker_user, _ = generate_user()
161 blockee_user, _ = generate_user()
163 # Users for reverse blocking tests
164 reverse_blocker, _ = generate_user()
165 reverse_blockee, _ = generate_user()
167 # Users for mutual blocking tests
168 mutual_blocker1, _ = generate_user()
169 mutual_blocker2, _ = generate_user()
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()
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()
185 # User for mixed test
186 mixed_deleted, _ = generate_user()
187 mixed_banned, _ = generate_user()
189 # Users for shadow tests
190 shadowed_user, _ = generate_user()
191 visible_for_shadowed, _ = generate_user()
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)
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
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
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)