Spaces:
Sleeping
Sleeping
| #!/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() |