Atlas / tests /test_user_id_validation.py
findEthics
feat: Add comprehensive database migration system for user authentication
04aa1ba
Raw
History Blame Contribute Delete
14.5 kB
#!/usr/bin/env python3
"""
Unit tests specifically for user_id validation in models
This test file focuses on comprehensive validation testing for the user_id field
across all analytics models (Session, Message, SearchAnalytics).
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# Load environment variables
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass # dotenv not available, continue without it
import unittest
from pydantic import ValidationError
from analytics.models import Session, Message, SearchAnalytics
class TestSessionUserIdValidation:
"""Test user_id validation in Session model"""
def test_valid_user_ids(self):
"""Test all valid user_id formats"""
valid_user_ids = [
"user123",
"user_123",
"user-123",
"user_123-test",
"123user",
"a", # Single character
"a" * 255, # Maximum length
None, # Anonymous user
]
for user_id in valid_user_ids:
session = Session(user_id=user_id)
assert session.user_id == user_id
def test_empty_string_becomes_none(self):
"""Test that empty strings are converted to None"""
test_cases = ["", " ", "\t", "\n", " \t\n "]
for empty_value in test_cases:
session = Session(user_id=empty_value)
assert session.user_id is None
def test_invalid_user_id_types(self):
"""Test that non-string types raise ValidationError"""
invalid_types = [123, 45.67, True, [], {}, object()]
for invalid_type in invalid_types:
try:
Session(user_id=invalid_type)
assert False, f"Should have raised ValidationError for {invalid_type}"
except Exception as e:
# Accept either ValueError or ValidationError
error_msg = str(e).lower()
assert ("user_id must be a string" in error_msg or
"string_type" in error_msg or
"should be a valid string" in error_msg), f"Unexpected error: {e}"
def test_user_id_too_long(self):
"""Test that user_id longer than 255 characters raises ValidationError"""
long_user_id = "a" * 256
try:
Session(user_id=long_user_id)
assert False, "Should have raised ValueError for too long user_id"
except Exception as e:
error_msg = str(e).lower()
assert ("user_id must be 255 characters or less" in error_msg or
"255" in error_msg), f"Unexpected error: {e}"
def test_invalid_characters(self):
"""Test that invalid characters raise ValidationError"""
invalid_user_ids = [
"user@123", # @ symbol
"user 123", # space
"user.123", # period
"user#123", # hash
"user$123", # dollar sign
"user%123", # percent
"user&123", # ampersand
"user*123", # asterisk
"user+123", # plus
"user=123", # equals
"user[123]", # brackets
"user{123}", # braces
"user|123", # pipe
"user\\123", # backslash
"user/123", # forward slash
"user:123", # colon
"user;123", # semicolon
"user<123>", # angle brackets
"user?123", # question mark
"user,123", # comma
"user'123", # single quote
'user"123', # double quote
"user`123", # backtick
"user~123", # tilde
"user!123", # exclamation
]
for invalid_user_id in invalid_user_ids:
try:
Session(user_id=invalid_user_id)
assert False, f"Should have raised ValueError for {invalid_user_id}"
except Exception as e:
error_msg = str(e).lower()
assert ("user_id can only contain alphanumeric characters" in error_msg or
"alphanumeric" in error_msg), f"Unexpected error for {invalid_user_id}: {e}"
def test_unicode_characters(self):
"""Test that unicode characters are rejected"""
unicode_user_ids = [
"user123é", # Accented character
"user123ñ", # Spanish character
"user123中", # Chinese character
"user123🚀", # Emoji
"user123α", # Greek character
]
for unicode_user_id in unicode_user_ids:
try:
Session(user_id=unicode_user_id)
assert False, f"Should have raised ValueError for {unicode_user_id}"
except Exception as e:
error_msg = str(e).lower()
assert ("user_id can only contain alphanumeric characters" in error_msg or
"alphanumeric" in error_msg), f"Unexpected error for {unicode_user_id}: {e}"
def test_edge_cases(self):
"""Test edge cases for user_id validation"""
# Test exactly 255 characters (should pass)
max_length_user_id = "a" * 255
session = Session(user_id=max_length_user_id)
assert session.user_id == max_length_user_id
# Test mixed valid characters
mixed_user_id = "user123_test-456"
session = Session(user_id=mixed_user_id)
assert session.user_id == mixed_user_id
# Test all numbers
numeric_user_id = "123456789"
session = Session(user_id=numeric_user_id)
assert session.user_id == numeric_user_id
# Test all underscores and hyphens
special_user_id = "___---___"
session = Session(user_id=special_user_id)
assert session.user_id == special_user_id
class TestMessageUserIdValidation:
"""Test user_id validation in Message model"""
def test_valid_user_ids(self):
"""Test valid user_id values in Message model"""
valid_user_ids = ["user123", "user_123", "user-123", None]
for user_id in valid_user_ids:
message = Message(
session_id="test_session",
prompt_length=50,
response_length=100,
response_time_ms=1000,
user_id=user_id
)
assert message.user_id == user_id
def test_invalid_user_ids(self):
"""Test invalid user_id values in Message model"""
try:
Message(
session_id="test_session",
prompt_length=50,
response_length=100,
response_time_ms=1000,
user_id=123
)
assert False, "Should have raised ValueError for non-string user_id"
except Exception as e:
error_msg = str(e).lower()
assert ("user_id must be a string" in error_msg or
"string_type" in error_msg or
"should be a valid string" in error_msg), f"Unexpected error: {e}"
try:
Message(
session_id="test_session",
prompt_length=50,
response_length=100,
response_time_ms=1000,
user_id="user@123"
)
assert False, "Should have raised ValueError for invalid characters"
except Exception as e:
error_msg = str(e).lower()
assert ("user_id can only contain alphanumeric characters" in error_msg or
"alphanumeric" in error_msg), f"Unexpected error: {e}"
def test_empty_string_handling(self):
"""Test empty string handling in Message model"""
message = Message(
session_id="test_session",
prompt_length=50,
response_length=100,
response_time_ms=1000,
user_id=""
)
assert message.user_id is None
class TestSearchAnalyticsUserIdValidation:
"""Test user_id validation in SearchAnalytics model"""
def test_valid_user_ids(self):
"""Test valid user_id values in SearchAnalytics model"""
valid_user_ids = ["user123", "user_123", "user-123", None]
for user_id in valid_user_ids:
search = SearchAnalytics(
message_id="test_message",
search_query="test query",
user_id=user_id
)
assert search.user_id == user_id
def test_invalid_user_ids(self):
"""Test invalid user_id values in SearchAnalytics model"""
try:
SearchAnalytics(
message_id="test_message",
search_query="test query",
user_id=123
)
assert False, "Should have raised ValueError for non-string user_id"
except Exception as e:
error_msg = str(e).lower()
assert ("user_id must be a string" in error_msg or
"string_type" in error_msg or
"should be a valid string" in error_msg), f"Unexpected error: {e}"
try:
SearchAnalytics(
message_id="test_message",
search_query="test query",
user_id="user@123"
)
assert False, "Should have raised ValueError for invalid characters"
except Exception as e:
error_msg = str(e).lower()
assert ("user_id can only contain alphanumeric characters" in error_msg or
"alphanumeric" in error_msg), f"Unexpected error: {e}"
def test_empty_string_handling(self):
"""Test empty string handling in SearchAnalytics model"""
search = SearchAnalytics(
message_id="test_message",
search_query="test query",
user_id=""
)
assert search.user_id is None
class TestModelToDictSerialization:
"""Test that to_dict() methods properly include user_id"""
def test_session_to_dict_with_user_id(self):
"""Test Session.to_dict() includes user_id"""
session = Session(user_id="user123")
session_dict = session.to_dict()
assert "user_id" in session_dict
assert session_dict["user_id"] == "user123"
assert session_dict["_id"] == session.session_id
def test_session_to_dict_without_user_id(self):
"""Test Session.to_dict() includes user_id as None"""
session = Session(user_id=None)
session_dict = session.to_dict()
assert "user_id" in session_dict
assert session_dict["user_id"] is None
def test_message_to_dict_with_user_id(self):
"""Test Message.to_dict() includes user_id"""
message = Message(
session_id="test_session",
prompt_length=50,
response_length=100,
response_time_ms=1000,
user_id="user123"
)
message_dict = message.to_dict()
assert "user_id" in message_dict
assert message_dict["user_id"] == "user123"
assert message_dict["_id"] == message.message_id
def test_message_to_dict_without_user_id(self):
"""Test Message.to_dict() includes user_id as None"""
message = Message(
session_id="test_session",
prompt_length=50,
response_length=100,
response_time_ms=1000,
user_id=None
)
message_dict = message.to_dict()
assert "user_id" in message_dict
assert message_dict["user_id"] is None
def test_search_analytics_to_dict_with_user_id(self):
"""Test SearchAnalytics.to_dict() includes user_id"""
search = SearchAnalytics(
message_id="test_message",
search_query="test query",
user_id="user123"
)
search_dict = search.to_dict()
assert "user_id" in search_dict
assert search_dict["user_id"] == "user123"
assert search_dict["_id"] == search.search_id
def test_search_analytics_to_dict_without_user_id(self):
"""Test SearchAnalytics.to_dict() includes user_id as None"""
search = SearchAnalytics(
message_id="test_message",
search_query="test query",
user_id=None
)
search_dict = search.to_dict()
assert "user_id" in search_dict
assert search_dict["user_id"] is None
def run_validation_tests():
"""Run all validation tests"""
print("🧪 Running User ID Validation Tests")
print("=" * 50)
# Test Session validation
session_test = TestSessionUserIdValidation()
session_test.test_valid_user_ids()
session_test.test_empty_string_becomes_none()
session_test.test_invalid_user_id_types()
session_test.test_user_id_too_long()
session_test.test_invalid_characters()
session_test.test_unicode_characters()
session_test.test_edge_cases()
print("✅ Session user_id validation tests passed")
# Test Message validation
message_test = TestMessageUserIdValidation()
message_test.test_valid_user_ids()
message_test.test_invalid_user_ids()
message_test.test_empty_string_handling()
print("✅ Message user_id validation tests passed")
# Test SearchAnalytics validation
search_test = TestSearchAnalyticsUserIdValidation()
search_test.test_valid_user_ids()
search_test.test_invalid_user_ids()
search_test.test_empty_string_handling()
print("✅ SearchAnalytics user_id validation tests passed")
# Test to_dict serialization
dict_test = TestModelToDictSerialization()
dict_test.test_session_to_dict_with_user_id()
dict_test.test_session_to_dict_without_user_id()
dict_test.test_message_to_dict_with_user_id()
dict_test.test_message_to_dict_without_user_id()
dict_test.test_search_analytics_to_dict_with_user_id()
dict_test.test_search_analytics_to_dict_without_user_id()
print("✅ Model to_dict serialization tests passed")
print("\n🎉 ALL VALIDATION TESTS PASSED!")
if __name__ == "__main__":
run_validation_tests()