Skip to content

Commit fd32c3e

Browse files
committed
coderabbit
1 parent e168276 commit fd32c3e

File tree

3 files changed

+58
-40
lines changed

3 files changed

+58
-40
lines changed

tests/conftest.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Global configuration for all tests."""
2+
3+
import os
4+
5+
# Set environment variable for all tests globally
6+
# This ensures the anonymization module can be imported in CI environments
7+
os.environ.setdefault("USER_ANON_PEPPER", "test-pepper-for-all-tests")

tests/unit/app/endpoints/test_query.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ async def _test_query_endpoint_handler(mocker, store_transcript_to_file=False):
169169
# Assert the store_transcript function is called if transcripts are enabled
170170
if store_transcript_to_file:
171171
mock_transcript.assert_called_once_with(
172-
user_id="mock_user_id",
172+
anonymous_user_id="mock_user_id",
173173
conversation_id=conversation_id,
174174
model_id="fake_model_id",
175175
provider_id="fake_provider_id",
@@ -1409,7 +1409,7 @@ def test_no_tools_parameter_backward_compatibility():
14091409
(
14101410
UserConversation(
14111411
id="conv1",
1412-
user_id="user1",
1412+
anonymous_user_id="user1",
14131413
last_used_provider="foo",
14141414
last_used_model="bar",
14151415
message_count=1,
@@ -1428,7 +1428,7 @@ def test_no_tools_parameter_backward_compatibility():
14281428
(
14291429
UserConversation(
14301430
id="conv1",
1431-
user_id="user1",
1431+
anonymous_user_id="user1",
14321432
last_used_provider="foo",
14331433
last_used_model="bar",
14341434
message_count=1,

tests/unit/utils/test_user_anonymization.py

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,8 @@
1010
from sqlalchemy.exc import IntegrityError
1111

1212
from models.database.user_mapping import UserMapping
13-
from utils.user_anonymization import (
14-
get_anonymous_user_id,
15-
get_user_count,
16-
find_anonymous_user_id,
17-
_hash_user_id,
18-
)
13+
14+
# Functions under test are accessed via a module fixture (see `ua` below)
1915

2016

2117
# Set up test environment variable for each test
@@ -31,29 +27,40 @@ def setup_test_pepper():
3127
yield
3228

3329

34-
class TestUserAnonymization:
30+
@pytest.fixture
31+
def user_anon_module():
32+
"""Return a fresh reference to the reloaded utils.user_anonymization module."""
33+
import utils.user_anonymization as _ua # pylint: disable=import-outside-toplevel
34+
35+
importlib.reload(_ua)
36+
return _ua
37+
38+
39+
class TestUserAnonymization: # pylint: disable=redefined-outer-name,protected-access
3540
"""Test user anonymization functionality."""
3641

37-
def test_hash_user_id_consistency(self):
42+
def test_hash_user_id_consistency(self, user_anon_module):
3843
"""Test that user ID hashing is consistent."""
3944
user_id = "[email protected]"
40-
hash1 = _hash_user_id(user_id)
41-
hash2 = _hash_user_id(user_id)
45+
hash1 = user_anon_module._hash_user_id(user_id)
46+
hash2 = user_anon_module._hash_user_id(user_id)
4247

4348
assert hash1 == hash2
4449
assert len(hash1) == 64 # SHA-256 hex length
4550
assert hash1 != user_id # Should be different from original
4651

47-
def test_hash_user_id_different_for_different_users(self):
52+
def test_hash_user_id_different_for_different_users(self, user_anon_module):
4853
"""Test that different user IDs produce different hashes."""
49-
user1_hash = _hash_user_id("[email protected]")
50-
user2_hash = _hash_user_id("[email protected]")
54+
user1_hash = user_anon_module._hash_user_id("[email protected]")
55+
user2_hash = user_anon_module._hash_user_id("[email protected]")
5156

5257
assert user1_hash != user2_hash
5358

5459
@patch("utils.user_anonymization.get_session")
5560
@patch("utils.user_anonymization.get_suid")
56-
def test_get_anonymous_user_id_new_user(self, mock_get_suid, mock_get_session):
61+
def test_get_anonymous_user_id_new_user(
62+
self, mock_get_suid, mock_get_session, user_anon_module
63+
):
5764
"""Test creating new anonymous ID for first-time user."""
5865
# Setup mocks
5966
mock_session = MagicMock()
@@ -62,7 +69,7 @@ def test_get_anonymous_user_id_new_user(self, mock_get_suid, mock_get_session):
6269
mock_get_suid.return_value = "anon-123-456"
6370

6471
user_id = "[email protected]"
65-
result = get_anonymous_user_id(user_id)
72+
result = user_anon_module.get_anonymous_user_id(user_id)
6673

6774
# Verify result
6875
assert result == "anon-123-456"
@@ -75,23 +82,27 @@ def test_get_anonymous_user_id_new_user(self, mock_get_suid, mock_get_session):
7582
added_mapping = mock_session.add.call_args[0][0]
7683
assert isinstance(added_mapping, UserMapping)
7784
assert added_mapping.anonymous_id == "anon-123-456"
78-
assert added_mapping.user_id_hash == _hash_user_id(user_id)
85+
assert added_mapping.user_id_hash == user_anon_module._hash_user_id(user_id)
7986

8087
@patch("utils.user_anonymization.get_session")
81-
def test_get_anonymous_user_id_existing_user(self, mock_get_session):
88+
def test_get_anonymous_user_id_existing_user(
89+
self, mock_get_session, user_anon_module
90+
):
8291
"""Test retrieving existing anonymous ID for returning user."""
8392
# Setup existing mapping
8493
existing_mapping = UserMapping()
8594
existing_mapping.anonymous_id = "existing-anon-789"
86-
existing_mapping.user_id_hash = _hash_user_id("[email protected]")
95+
existing_mapping.user_id_hash = user_anon_module._hash_user_id(
96+
97+
)
8798

8899
mock_session = MagicMock()
89100
mock_get_session.return_value.__enter__.return_value = mock_session
90101
mock_session.query.return_value.filter_by.return_value.first.return_value = (
91102
existing_mapping
92103
)
93104

94-
result = get_anonymous_user_id("[email protected]")
105+
result = user_anon_module.get_anonymous_user_id("[email protected]")
95106

96107
# Verify result
97108
assert result == "existing-anon-789"
@@ -103,7 +114,7 @@ def test_get_anonymous_user_id_existing_user(self, mock_get_session):
103114
@patch("utils.user_anonymization.get_session")
104115
@patch("utils.user_anonymization.get_suid")
105116
def test_get_anonymous_user_id_race_condition(
106-
self, mock_get_suid, mock_get_session
117+
self, mock_get_suid, mock_get_session, user_anon_module
107118
):
108119
"""Test handling race condition when creating user mapping."""
109120
# Setup mocks for race condition scenario
@@ -123,7 +134,7 @@ def test_get_anonymous_user_id_race_condition(
123134
mock_session.add.side_effect = IntegrityError("Duplicate key", None, None)
124135
mock_get_suid.return_value = "new-uuid"
125136

126-
result = get_anonymous_user_id("[email protected]")
137+
result = user_anon_module.get_anonymous_user_id("[email protected]")
127138

128139
# Should return the mapping created by the other thread
129140
assert result == "race-condition-uuid"
@@ -132,7 +143,7 @@ def test_get_anonymous_user_id_race_condition(
132143
@patch("utils.user_anonymization.get_session")
133144
@patch("utils.user_anonymization.get_suid")
134145
def test_get_anonymous_user_id_race_condition_failure(
135-
self, mock_get_suid, mock_get_session
146+
self, mock_get_suid, mock_get_session, user_anon_module
136147
):
137148
"""Test handling race condition where retrieval also fails."""
138149
mock_session = MagicMock()
@@ -149,22 +160,22 @@ def test_get_anonymous_user_id_race_condition_failure(
149160
with pytest.raises(
150161
RuntimeError, match="Unable to create or retrieve anonymous user ID"
151162
):
152-
get_anonymous_user_id("[email protected]")
163+
user_anon_module.get_anonymous_user_id("[email protected]")
153164

154165
@patch("utils.user_anonymization.get_session")
155-
def test_get_user_count(self, mock_get_session):
166+
def test_get_user_count(self, mock_get_session, user_anon_module):
156167
"""Test getting total user count."""
157168
mock_session = MagicMock()
158169
mock_get_session.return_value.__enter__.return_value = mock_session
159170
mock_session.query.return_value.count.return_value = 42
160171

161-
result = get_user_count()
172+
result = user_anon_module.get_user_count()
162173

163174
assert result == 42
164175
mock_session.query.assert_called_once_with(UserMapping)
165176

166177
@patch("utils.user_anonymization.get_session")
167-
def test_find_anonymous_user_id_existing(self, mock_get_session):
178+
def test_find_anonymous_user_id_existing(self, mock_get_session, user_anon_module):
168179
"""Test finding existing anonymous ID without creating new one."""
169180
existing_mapping = UserMapping()
170181
existing_mapping.anonymous_id = "found-uuid"
@@ -175,28 +186,28 @@ def test_find_anonymous_user_id_existing(self, mock_get_session):
175186
existing_mapping
176187
)
177188

178-
result = find_anonymous_user_id("[email protected]")
189+
result = user_anon_module.find_anonymous_user_id("[email protected]")
179190

180191
assert result == "found-uuid"
181192

182193
@patch("utils.user_anonymization.get_session")
183-
def test_find_anonymous_user_id_not_found(self, mock_get_session):
194+
def test_find_anonymous_user_id_not_found(self, mock_get_session, user_anon_module):
184195
"""Test finding non-existing anonymous ID returns None."""
185196
mock_session = MagicMock()
186197
mock_get_session.return_value.__enter__.return_value = mock_session
187198
mock_session.query.return_value.filter_by.return_value.first.return_value = None
188199

189-
result = find_anonymous_user_id("[email protected]")
200+
result = user_anon_module.find_anonymous_user_id("[email protected]")
190201

191202
assert result is None
192203

193-
def test_hmac_prevents_rainbow_attacks(self):
204+
def test_hmac_prevents_rainbow_attacks(self, user_anon_module):
194205
"""Test that HMAC makes rainbow table attacks impractical."""
195206
# Common passwords/emails that might be in rainbow tables
196207
common_ids = ["admin", "[email protected]", "user123", "[email protected]"]
197208

198209
for user_id in common_ids:
199-
hash_result = _hash_user_id(user_id)
210+
hash_result = user_anon_module._hash_user_id(user_id)
200211
# Hash should not match simple SHA-256 of just the user ID
201212
simple_hash = hashlib.sha256(user_id.encode()).hexdigest()
202213
assert hash_result != simple_hash
@@ -207,7 +218,7 @@ def test_hmac_prevents_rainbow_attacks(self):
207218
).hexdigest()
208219
assert hash_result != hmac_without_pepper
209220

210-
def test_anonymization_preserves_uniqueness(self):
221+
def test_anonymization_preserves_uniqueness(self, user_anon_module):
211222
"""Test that different users get different anonymous IDs."""
212223
users = [
213224
@@ -216,12 +227,12 @@ def test_anonymization_preserves_uniqueness(self):
216227
217228
]
218229

219-
hashes = [_hash_user_id(user) for user in users]
230+
hashes = [user_anon_module._hash_user_id(user) for user in users]
220231

221232
# All hashes should be unique
222233
assert len(set(hashes)) == len(hashes)
223234

224-
def test_user_id_normalization(self):
235+
def test_user_id_normalization(self, user_anon_module):
225236
"""Test that user ID normalization works correctly."""
226237
# Test case variations should produce the same hash
227238
variations = [
@@ -232,15 +243,15 @@ def test_user_id_normalization(self):
232243
233244
]
234245

235-
hashes = [_hash_user_id(variation) for variation in variations]
246+
hashes = [user_anon_module._hash_user_id(variation) for variation in variations]
236247

237248
# All variations should produce the same hash
238249
assert (
239250
len(set(hashes)) == 1
240251
), "All case/whitespace variations should hash the same"
241252

242253
# But different actual users should still be different
243-
different_user = _hash_user_id("[email protected]")
254+
different_user = user_anon_module._hash_user_id("[email protected]")
244255
assert different_user not in hashes
245256

246257
def test_missing_pepper_env_var(self):
@@ -256,7 +267,7 @@ def test_missing_pepper_env_var(self):
256267
importlib.reload(utils.user_anonymization)
257268

258269

259-
class TestUserMappingModel:
270+
class TestUserMappingModel: # pylint: disable=redefined-outer-name
260271
"""Test the UserMapping database model."""
261272

262273
def test_user_mapping_attributes(self):

0 commit comments

Comments
 (0)