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