CoolFace
Apppublic

findEthics/Atlas

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
test_authentication.py592 linesDownload Raw Back to unit
1"""2Unit tests for user authentication functionality3 4Consolidated from:5- test_user_authentication_comprehensive.py (unit test portions)6- test_user_id_validation.py7- Parts of test_chat_integration_user_auth.py (validation portions)8"""9 10import asyncio11 12from app import normalize_user_id, validate_user_id13import pytest14 15from analytics.collectors import create_session, track_message, track_search16from analytics.models import Message, SearchAnalytics, Session17from tests.utilities import MockDataGenerator, TestHelpers, ValidationHelpers, empty_user_ids, invalid_user_ids, valid_user_ids18 19    TestHelpers, ValidationHelpers, MockDataGenerator,20    valid_user_ids, invalid_user_ids, empty_user_ids21)22 23 24class TestUserIdValidation:25    """Test user_id validation functions and model validation"""26    27    def test_normalize_user_id_function(self):28        """Test normalize_user_id function handles various inputs correctly"""29        # Test None input30        assert normalize_user_id(None) is None31        32        # Test empty string33        assert normalize_user_id("") is None34        35        # Test whitespace-only strings36        assert normalize_user_id("   ") is None37        assert normalize_user_id("\t") is None38        assert normalize_user_id("\n") is None39        assert normalize_user_id("  \t\n  ") is None40        41        # Test valid user_id42        assert normalize_user_id("user123") == "user123"43        assert normalize_user_id("  user123  ") == "user123"44    45    def test_validate_user_id_function(self):46        """Test validate_user_id function properly handles all cases"""47        # Test None input (anonymous)48        assert validate_user_id(None) is None49        50        # Test empty string (anonymous)51        assert validate_user_id("") is None52        assert validate_user_id("   ") is None53        54        # Test valid user_id55        assert validate_user_id("user123") == "user123"56        assert validate_user_id("user_123") == "user_123"57        assert validate_user_id("user-123") == "user-123"58        59        # Test invalid user_id raises exception60        with pytest.raises(Exception):61            validate_user_id("user@123")62        63        with pytest.raises(Exception):64            validate_user_id("a" * 256)  # Too long65    66    def test_validate_user_id_with_fixtures(self, valid_user_ids, invalid_user_ids, empty_user_ids):67        """Test validate_user_id with fixture data"""68        # Test valid user IDs69        for user_id in valid_user_ids:70            result = validate_user_id(user_id)71            assert result == user_id.strip()72        73        # Test invalid user IDs74        for user_id in invalid_user_ids:75            with pytest.raises(Exception):76                validate_user_id(user_id)77        78        # Test empty user IDs (should return None)79        for user_id in empty_user_ids:80            result = validate_user_id(user_id)81            assert result is None82 83 84class TestSessionModelValidation:85    """Test Session model user_id validation"""86    87    def test_session_valid_user_id(self):88        """Test Session model with valid user_id values"""89        # Valid user_id90        session = Session(user_id="user_123")91        assert session.user_id == "user_123"92        93        # Valid user_id with hyphens and underscores94        session = Session(user_id="user-123_test")95        assert session.user_id == "user-123_test"96        97        # None user_id (anonymous)98        session = Session(user_id=None)99        assert session.user_id is None100        101        # Empty string becomes None102        session = Session(user_id="")103        assert session.user_id is None104        105        # Whitespace-only string becomes None106        session = Session(user_id="   ")107        assert session.user_id is None108    109    def test_session_invalid_user_id(self):110        """Test Session model with invalid user_id values"""111        # Non-string user_id112        with pytest.raises(Exception) as exc_info:113            Session(user_id=123)114        assert "string" in str(exc_info.value).lower()115        116        # Too long user_id117        with pytest.raises(Exception) as exc_info:118            Session(user_id="a" * 256)119        assert "255" in str(exc_info.value)120        121        # Invalid characters122        invalid_ids = ["user@123", "user 123", "user.123"]123        for invalid_id in invalid_ids:124            with pytest.raises(Exception) as exc_info:125                Session(user_id=invalid_id)126            assert "alphanumeric" in str(exc_info.value).lower()127    128    def test_session_to_dict_includes_user_id(self):129        """Test that Session.to_dict() includes user_id field"""130        # Session with user_id131        session = Session(user_id="user_123")132        session_dict = session.to_dict()133        assert "user_id" in session_dict134        assert session_dict["user_id"] == "user_123"135        136        # Anonymous session137        session = Session(user_id=None)138        session_dict = session.to_dict()139        assert "user_id" in session_dict140        assert session_dict["user_id"] is None141 142 143class TestMessageModelValidation:144    """Test Message model user_id validation"""145    146    def test_message_valid_user_id(self):147        """Test Message model with valid user_id values"""148        # Valid user_id149        message = Message(150            session_id="test_session",151            prompt_length=50,152            response_length=100,153            response_time_ms=1000,154            user_id="user_123"155        )156        assert message.user_id == "user_123"157        158        # None user_id (anonymous)159        message = Message(160            session_id="test_session",161            prompt_length=50,162            response_length=100,163            response_time_ms=1000,164            user_id=None165        )166        assert message.user_id is None167    168    def test_message_invalid_user_id(self):169        """Test Message model with invalid user_id values"""170        # Non-string user_id171        with pytest.raises(Exception) as exc_info:172            Message(173                session_id="test_session",174                prompt_length=50,175                response_length=100,176                response_time_ms=1000,177                user_id=123178            )179        assert "string" in str(exc_info.value).lower()180        181        # Invalid characters182        with pytest.raises(Exception):183            Message(184                session_id="test_session",185                prompt_length=50,186                response_length=100,187                response_time_ms=1000,188                user_id="user@123"189            )190    191    def test_message_to_dict_includes_user_id(self):192        """Test that Message.to_dict() includes user_id field"""193        message = Message(194            session_id="test_session",195            prompt_length=50,196            response_length=100,197            response_time_ms=1000,198            user_id="user_123"199        )200        message_dict = message.to_dict()201        assert "user_id" in message_dict202        assert message_dict["user_id"] == "user_123"203 204 205class TestSearchAnalyticsModelValidation:206    """Test SearchAnalytics model user_id validation"""207    208    def test_search_analytics_valid_user_id(self):209        """Test SearchAnalytics model with valid user_id values"""210        # Valid user_id211        search = SearchAnalytics(212            message_id="test_message",213            search_query="test query",214            user_id="user_123"215        )216        assert search.user_id == "user_123"217        218        # None user_id (anonymous)219        search = SearchAnalytics(220            message_id="test_message",221            search_query="test query",222            user_id=None223        )224        assert search.user_id is None225    226    def test_search_analytics_invalid_user_id(self):227        """Test SearchAnalytics model with invalid user_id values"""228        # Non-string user_id229        with pytest.raises(Exception) as exc_info:230            SearchAnalytics(231                message_id="test_message",232                search_query="test query",233                user_id=123234            )235        assert "string" in str(exc_info.value).lower()236        237        # Invalid characters238        with pytest.raises(Exception):239            SearchAnalytics(240                message_id="test_message",241                search_query="test query",242                user_id="user@123"243            )244    245    def test_search_analytics_to_dict_includes_user_id(self):246        """Test that SearchAnalytics.to_dict() includes user_id field"""247        search = SearchAnalytics(248            message_id="test_message",249            search_query="test query",250            user_id="user_123"251        )252        search_dict = search.to_dict()253        assert "user_id" in search_dict254        assert search_dict["user_id"] == "user_123"255 256 257class TestAnalyticsCollectors:258    """Test analytics collectors with user_id support"""259    260    @pytest.mark.asyncio261    async def test_create_session_with_user_id(self):262        """Test create_session function with user_id"""263        # Create session with user_id264        session = await create_session(user_agent="TestAgent", user_id="user_123")265        assert session.user_id == "user_123"266        assert session.user_agent == "TestAgent"267        assert session.session_id is not None268        269        # Create anonymous session270        session = await create_session(user_agent="TestAgent", user_id=None)271        assert session.user_id is None272        273        # Create session without user_id parameter274        session = await create_session(user_agent="TestAgent")275        assert session.user_id is None276    277    @pytest.mark.asyncio278    async def test_track_message_with_user_id(self):279        """Test track_message function with user_id"""280        # Create a session first281        session = await create_session(user_id="user_123")282        283        # Track message with user_id284        message = await track_message(285            session_id=session.session_id,286            prompt_length=50,287            response_length=100,288            response_time_ms=1000,289            user_id="user_123"290        )291        292        assert message is not None293        assert message.user_id == "user_123"294        assert message.session_id == session.session_id295        assert message.prompt_length == 50296        assert message.response_length == 100297        assert message.response_time_ms == 1000298    299    @pytest.mark.asyncio300    async def test_track_message_user_id_mismatch(self):301        """Test that user_id mismatch between session and message is handled"""302        # Create a session with one user_id303        session = await create_session(user_id="user_123")304        305        # Track message with different user_id306        message = await track_message(307            session_id=session.session_id,308            prompt_length=50,309            response_length=100,310            response_time_ms=1000,311            user_id="user_456"  # Different user_id312        )313        314        assert message is not None315        assert message.user_id == "user_456"  # Message should use provided user_id316        assert message.session_id == session.session_id317    318    @pytest.mark.asyncio319    async def test_track_search_with_user_id(self):320        """Test track_search function with user_id"""321        # Create session and message first322        session = await create_session(user_id="user_123")323        message = await track_message(324            session_id=session.session_id,325            prompt_length=50,326            response_length=100,327            response_time_ms=1000,328            user_id="user_123"329        )330        331        # Track search with user_id332        search = await track_search(333            message_id=message.message_id,334            search_query="test query",335            search_terms=["test", "query"],336            brave_results=5,337            duckduckgo_results=3,338            total_unique_results=7,339            user_id="user_123"340        )341        342        assert search is not None343        assert search.user_id == "user_123"344        assert search.message_id == message.message_id345        assert search.search_query == "test query"346    347    @pytest.mark.asyncio348    async def test_track_search_user_id_mismatch(self):349        """Test track_search with user_id mismatch"""350        # Create session and message with one user_id351        session = await create_session(user_id="user_123")352        message = await track_message(353            session_id=session.session_id,354            prompt_length=50,355            response_length=100,356            response_time_ms=1000,357            user_id="user_123"358        )359        360        # Track search with different user_id361        search = await track_search(362            message_id=message.message_id,363            search_query="test query",364            search_terms=["test", "query"],365            user_id="user_456"  # Different user_id366        )367        368        assert search is not None369        assert search.user_id == "user_456"  # Should use provided user_id370        assert search.message_id == message.message_id371 372 373class TestBackwardCompatibility:374    """Test backward compatibility for anonymous users"""375    376    @pytest.mark.asyncio377    async def test_create_session_without_user_id_parameter(self):378        """Test creating sessions without user_id parameter (old way)"""379        # Create session the old way (no user_id parameter)380        session = await create_session(user_agent="TestAgent")381        382        assert session.user_id is None383        assert session.user_agent == "TestAgent"384        assert session.session_id is not None385        assert session.status == "active"386    387    @pytest.mark.asyncio388    async def test_track_message_without_user_id_parameter(self):389        """Test tracking messages without user_id parameter (old way)"""390        # Create session first391        session = await create_session(user_agent="TestAgent")392        393        # Track message the old way (no user_id parameter)394        message = await track_message(395            session_id=session.session_id,396            prompt_length=50,397            response_length=100,398            response_time_ms=1000,399            used_search=True,400            max_tokens=500,401            temperature=0.7,402            success=True403        )404        405        assert message is not None406        assert message.user_id is None407        assert message.session_id == session.session_id408        assert message.prompt_length == 50409        assert message.used_search is True410    411    @pytest.mark.asyncio412    async def test_track_search_without_user_id_parameter(self):413        """Test tracking search without user_id parameter (old way)"""414        # Create session and message first415        session = await create_session()416        message = await track_message(417            session_id=session.session_id,418            prompt_length=50,419            response_length=100,420            response_time_ms=1000421        )422        423        # Track search the old way (no user_id parameter)424        search = await track_search(425            message_id=message.message_id,426            search_query="test query",427            search_terms=["test", "query"],428            brave_results=5,429            duckduckgo_results=3,430            total_unique_results=7431        )432        433        assert search is not None434        assert search.user_id is None435        assert search.message_id == message.message_id436        assert search.search_query == "test query"437 438 439class TestAuthenticationEdgeCases:440    """Test edge cases and error conditions"""441    442    def test_user_id_length_limits(self):443        """Test user_id length validation"""444        # Test maximum valid length445        max_valid = "a" * 255446        session = Session(user_id=max_valid)447        assert session.user_id == max_valid448        449        # Test too long450        too_long = "a" * 256451        with pytest.raises(Exception) as exc_info:452            Session(user_id=too_long)453        assert "255" in str(exc_info.value)454    455    def test_user_id_character_validation(self):456        """Test user_id character validation"""457        # Valid characters458        valid_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"459        session = Session(user_id=valid_chars)460        assert session.user_id == valid_chars461        462        # Invalid characters463        invalid_chars = ["@", " ", ".", "#", "$", "%", "&", "*", "+", "=", "[", "]", "{", "}", "|", "\\", "/", ":", ";", "<", ">", "?", ",", "'", '"', "`", "~", "!"]464        465        for char in invalid_chars:466            user_id_with_invalid = f"user{char}123"467            with pytest.raises(Exception):468                Session(user_id=user_id_with_invalid)469    470    def test_user_id_type_validation(self):471        """Test user_id type validation"""472        # Valid types473        Session(user_id="string_user_id")474        Session(user_id=None)475        476        # Invalid types477        invalid_types = [123, 45.67, True, [], {}, set()]478        479        for invalid_type in invalid_types:480            with pytest.raises(Exception):481                Session(user_id=invalid_type)482    483    @pytest.mark.asyncio484    async def test_collectors_with_invalid_user_id(self):485        """Test that collectors handle invalid user_id gracefully"""486        # Test create_session with invalid user_id487        with pytest.raises(Exception):488            await create_session(user_id="invalid@user")489        490        # Test track_message with invalid user_id491        session = await create_session(user_id="valid_user")492        with pytest.raises(Exception):493            await track_message(494                session_id=session.session_id,495                prompt_length=50,496                response_length=100,497                response_time_ms=1000,498                user_id="invalid@user"499            )500 501 502class TestAuthenticationDataConsistency:503    """Test data consistency across authentication features"""504    505    @pytest.mark.asyncio506    async def test_session_message_user_id_consistency(self):507        """Test that session and message user_id values are consistent"""508        user_id = "consistency_test_user"509        510        # Create session511        session = await create_session(user_id=user_id)512        assert session.user_id == user_id513        514        # Create message with same user_id515        message = await track_message(516            session_id=session.session_id,517            prompt_length=50,518            response_length=100,519            response_time_ms=1000,520            user_id=user_id521        )522        assert message.user_id == user_id523        assert message.session_id == session.session_id524        525        # Verify data persistence526        await TestHelpers.wait_for_data_persistence()527        528        # Verify session data529        session_valid = await TestHelpers.verify_session_data(530            session.session_id, user_id, expected_messages=1531        )532        assert session_valid533        534        # Verify message data535        message_valid = await TestHelpers.verify_message_data(536            message.message_id, user_id537        )538        assert message_valid539    540    @pytest.mark.asyncio541    async def test_anonymous_data_consistency(self):542        """Test that anonymous data is consistently handled"""543        # Create anonymous session544        session = await create_session(user_id=None)545        assert session.user_id is None546        547        # Create anonymous message548        message = await track_message(549            session_id=session.session_id,550            prompt_length=50,551            response_length=100,552            response_time_ms=1000,553            user_id=None554        )555        assert message.user_id is None556        assert message.session_id == session.session_id557        558        # Verify data persistence559        await TestHelpers.wait_for_data_persistence()560        561        # Verify session data562        session_valid = await TestHelpers.verify_session_data(563            session.session_id, None, expected_messages=1564        )565        assert session_valid566        567        # Verify message data568        message_valid = await TestHelpers.verify_message_data(569            message.message_id, None570        )571        assert message_valid572 573 574if __name__ == "__main__":575    # Run tests manually for debugging576    async def run_basic_tests():577        test_validation = TestUserIdValidation()578        test_validation.test_normalize_user_id_function()579        test_validation.test_validate_user_id_function()580        print("✅ User ID validation tests passed")581        582        test_session = TestSessionModelValidation()583        test_session.test_session_valid_user_id()584        test_session.test_session_to_dict_includes_user_id()585        print("✅ Session model validation tests passed")586        587        test_collectors = TestAnalyticsCollectors()588        await test_collectors.test_create_session_with_user_id()589        await test_collectors.test_track_message_with_user_id()590        print("✅ Analytics collectors tests passed")591    592    asyncio.run(run_basic_tests())