findEthics/Atlas
0
1#!/usr/bin/env python32"""3Backward compatibility tests for user authentication feature4 5This test file ensures that existing anonymous user workflows continue to work6exactly as they did before the user authentication feature was added.7"""8 9import sys10import os11sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))12 13# Load environment variables14try:15 from dotenv import load_dotenv16 load_dotenv()17except ImportError:18 pass # dotenv not available, continue without it19 20import asyncio21import httpx22import time23from datetime import datetime24from typing import Optional, Dict, Any25 26from analytics.collectors import create_session, track_message, track_search27from analytics.dashboard import get_basic_stats, get_hourly_message_stats, get_session_stats28from analytics.database import get_sessions_collection, get_messages_collection, get_search_analytics_collection29 30 31class TestAnonymousUserCompatibility:32 """Test that anonymous users work exactly as before"""33 34 async def test_create_session_without_user_id(self):35 """Test creating sessions without user_id parameter (old way)"""36 # Create session the old way (no user_id parameter)37 session = await create_session(user_agent="TestAgent")38 39 assert session.user_id is None40 assert session.user_agent == "TestAgent"41 assert session.session_id is not None42 assert session.status == "active"43 44 print("✅ Anonymous session creation works as before")45 46 async def test_create_session_with_none_user_id(self):47 """Test creating sessions with explicit None user_id"""48 # Create session with explicit None49 session = await create_session(user_agent="TestAgent", user_id=None)50 51 assert session.user_id is None52 assert session.user_agent == "TestAgent"53 assert session.session_id is not None54 assert session.status == "active"55 56 print("✅ Session creation with None user_id works")57 58 async def test_track_message_without_user_id(self):59 """Test tracking messages without user_id parameter (old way)"""60 # Create session first61 session = await create_session(user_agent="TestAgent")62 63 # Track message the old way (no user_id parameter)64 message = await track_message(65 session_id=session.session_id,66 prompt_length=50,67 response_length=100,68 response_time_ms=1000,69 used_search=True,70 max_tokens=500,71 temperature=0.7,72 success=True73 )74 75 assert message is not None76 assert message.user_id is None77 assert message.session_id == session.session_id78 assert message.prompt_length == 5079 assert message.response_length == 10080 assert message.used_search is True81 82 print("✅ Anonymous message tracking works as before")83 84 async def test_track_message_with_none_user_id(self):85 """Test tracking messages with explicit None user_id"""86 # Create session first87 session = await create_session()88 89 # Track message with explicit None user_id90 message = await track_message(91 session_id=session.session_id,92 prompt_length=40,93 response_length=80,94 response_time_ms=800,95 used_search=False,96 user_id=None97 )98 99 assert message is not None100 assert message.user_id is None101 assert message.session_id == session.session_id102 103 print("✅ Message tracking with None user_id works")104 105 async def test_track_search_without_user_id(self):106 """Test tracking search without user_id parameter (old way)"""107 # Create session and message first108 session = await create_session()109 message = await track_message(110 session_id=session.session_id,111 prompt_length=50,112 response_length=100,113 response_time_ms=1000114 )115 116 # Track search the old way (no user_id parameter)117 search = await track_search(118 message_id=message.message_id,119 search_query="test query",120 search_terms=["test", "query"],121 brave_results=5,122 duckduckgo_results=3,123 total_unique_results=7,124 brave_response_time_ms=1000,125 duckduckgo_response_time_ms=800,126 search_engines_used=["brave", "duckduckgo"],127 search_success=True,128 fallback_used=False129 )130 131 assert search is not None132 assert search.user_id is None133 assert search.message_id == message.message_id134 assert search.search_query == "test query"135 136 print("✅ Anonymous search tracking works as before")137 138 async def test_track_search_with_none_user_id(self):139 """Test tracking search with explicit None user_id"""140 # Create session and message first141 session = await create_session()142 message = await track_message(143 session_id=session.session_id,144 prompt_length=50,145 response_length=100,146 response_time_ms=1000147 )148 149 # Track search with explicit None user_id150 search = await track_search(151 message_id=message.message_id,152 search_query="test query",153 search_terms=["test", "query"],154 user_id=None155 )156 157 assert search is not None158 assert search.user_id is None159 160 print("✅ Search tracking with None user_id works")161 162 163class TestAnonymousChatRequests:164 """Test that anonymous chat requests work as before"""165 166 async def test_chat_request_without_user_id_field(self):167 """Test chat request without user_id field (old API format)"""168 chat_data = {169 "prompt": "Test anonymous chat request",170 "max_new_tokens": 100,171 "use_search": False,172 "temperature": 0.7173 # No user_id field - this is the old format174 }175 176 try:177 async with httpx.AsyncClient(timeout=30.0) as client:178 response = await client.post(179 "http://localhost:7860/chat",180 json=chat_data,181 headers={"Content-Type": "application/json"}182 )183 184 assert response.status_code == 200185 result = response.json()186 assert "response" in result187 assert isinstance(result["response"], str)188 assert len(result["response"]) > 0189 190 # Check session ID in headers191 session_id = response.headers.get('X-Session-ID')192 assert session_id is not None193 194 print("✅ Anonymous chat request (old format) works")195 return session_id196 197 except httpx.ConnectError:198 print("⚠️ Server not running - skipping chat request test")199 return None200 201 async def test_chat_request_with_null_user_id(self):202 """Test chat request with null user_id"""203 chat_data = {204 "prompt": "Test chat request with null user_id",205 "max_new_tokens": 100,206 "use_search": False,207 "temperature": 0.7,208 "user_id": None209 }210 211 try:212 async with httpx.AsyncClient(timeout=30.0) as client:213 response = await client.post(214 "http://localhost:7860/chat",215 json=chat_data,216 headers={"Content-Type": "application/json"}217 )218 219 assert response.status_code == 200220 result = response.json()221 assert "response" in result222 223 print("✅ Chat request with null user_id works")224 225 except httpx.ConnectError:226 print("⚠️ Server not running - skipping chat request test")227 228 async def test_multiple_anonymous_requests(self):229 """Test multiple anonymous requests work as before"""230 try:231 async with httpx.AsyncClient(timeout=30.0) as client:232 # First anonymous request233 chat_data1 = {234 "prompt": "First anonymous message",235 "max_new_tokens": 50,236 "use_search": False,237 "temperature": 0.7238 }239 240 response1 = await client.post(241 "http://localhost:7860/chat",242 json=chat_data1,243 headers={"Content-Type": "application/json"}244 )245 246 assert response1.status_code == 200247 session_id1 = response1.headers.get('X-Session-ID')248 249 # Second anonymous request (different session)250 chat_data2 = {251 "prompt": "Second anonymous message",252 "max_new_tokens": 50,253 "use_search": False,254 "temperature": 0.7255 }256 257 response2 = await client.post(258 "http://localhost:7860/chat",259 json=chat_data2,260 headers={"Content-Type": "application/json"}261 )262 263 assert response2.status_code == 200264 session_id2 = response2.headers.get('X-Session-ID')265 266 # Should get different sessions (as before)267 assert session_id1 != session_id2268 269 print("✅ Multiple anonymous requests work as before")270 271 except httpx.ConnectError:272 print("⚠️ Server not running - skipping multiple requests test")273 274 async def test_anonymous_session_continuation(self):275 """Test that anonymous sessions can be continued with session ID"""276 try:277 async with httpx.AsyncClient(timeout=30.0) as client:278 # First request creates session279 chat_data1 = {280 "prompt": "First message in session",281 "max_new_tokens": 50,282 "use_search": False,283 "temperature": 0.7284 }285 286 response1 = await client.post(287 "http://localhost:7860/chat",288 json=chat_data1,289 headers={"Content-Type": "application/json"}290 )291 292 assert response1.status_code == 200293 session_id = response1.headers.get('X-Session-ID')294 295 # Second request continues same session296 chat_data2 = {297 "prompt": "Second message in same session",298 "max_new_tokens": 50,299 "use_search": True,300 "temperature": 0.7301 }302 303 response2 = await client.post(304 "http://localhost:7860/chat",305 json=chat_data2,306 headers={307 "Content-Type": "application/json",308 "X-Session-ID": session_id309 }310 )311 312 assert response2.status_code == 200313 session_id2 = response2.headers.get('X-Session-ID')314 315 # Should be same session316 assert session_id2 == session_id317 318 print("✅ Anonymous session continuation works as before")319 320 except httpx.ConnectError:321 print("⚠️ Server not running - skipping session continuation test")322 323 324class TestAnalyticsFunctionCompatibility:325 """Test that analytics functions work with anonymous data"""326 327 async def test_basic_stats_with_anonymous_data(self):328 """Test that get_basic_stats works with anonymous data"""329 # Create some anonymous data330 session = await create_session()331 await track_message(332 session_id=session.session_id,333 prompt_length=50,334 response_length=100,335 response_time_ms=1000336 )337 338 # Test basic stats function339 stats = await get_basic_stats()340 341 assert isinstance(stats, dict)342 assert "total_sessions" in stats343 assert "total_messages" in stats344 assert "active_sessions" in stats345 assert stats["total_sessions"] >= 1346 assert stats["total_messages"] >= 1347 348 print("✅ Basic stats work with anonymous data")349 350 async def test_hourly_stats_with_anonymous_data(self):351 """Test that get_hourly_message_stats works with anonymous data"""352 # Create some anonymous data353 session = await create_session()354 await track_message(355 session_id=session.session_id,356 prompt_length=50,357 response_length=100,358 response_time_ms=1000359 )360 361 # Test hourly stats function362 hourly_stats = await get_hourly_message_stats(hours=24)363 364 assert isinstance(hourly_stats, list)365 # Should have 24 hours of data366 assert len(hourly_stats) == 24367 368 for hour_data in hourly_stats:369 assert "hour" in hour_data370 assert "message_count" in hour_data371 assert "search_count" in hour_data372 assert "avg_response_time_ms" in hour_data373 374 print("✅ Hourly stats work with anonymous data")375 376 async def test_session_stats_with_anonymous_data(self):377 """Test that get_session_stats works with anonymous data"""378 # Create some anonymous data379 session = await create_session()380 await track_message(381 session_id=session.session_id,382 prompt_length=50,383 response_length=100,384 response_time_ms=1000385 )386 387 # Test session stats function388 session_stats = await get_session_stats()389 390 assert isinstance(session_stats, dict)391 assert "total_sessions" in session_stats392 assert "active_sessions" in session_stats393 assert "ended_sessions" in session_stats394 assert session_stats["total_sessions"] >= 1395 396 print("✅ Session stats work with anonymous data")397 398 399class TestDatabaseCompatibility:400 """Test that database operations work with anonymous data"""401 402 async def test_anonymous_data_storage(self):403 """Test that anonymous data is stored correctly in database"""404 # Create anonymous session and message405 session = await create_session(user_agent="TestAgent")406 message = await track_message(407 session_id=session.session_id,408 prompt_length=50,409 response_length=100,410 response_time_ms=1000411 )412 413 # Wait for data to be written414 await asyncio.sleep(1)415 416 # Check database storage417 sessions_collection = await get_sessions_collection()418 messages_collection = await get_messages_collection()419 420 if sessions_collection and messages_collection:421 # Check session document422 session_doc = await sessions_collection.find_one({"_id": session.session_id})423 assert session_doc is not None424 assert session_doc.get("user_id") is None425 assert session_doc.get("user_agent") == "TestAgent"426 427 # Check message document428 message_doc = await messages_collection.find_one({"_id": message.message_id})429 assert message_doc is not None430 assert message_doc.get("user_id") is None431 assert message_doc.get("session_id") == session.session_id432 433 print("✅ Anonymous data stored correctly in database")434 else:435 print("⚠️ Database not available - skipping storage test")436 437 async def test_anonymous_data_queries(self):438 """Test that queries work correctly with anonymous data"""439 # Create anonymous data440 session = await create_session()441 await track_message(442 session_id=session.session_id,443 prompt_length=50,444 response_length=100,445 response_time_ms=1000446 )447 448 # Wait for data to be written449 await asyncio.sleep(1)450 451 # Test queries452 sessions_collection = await get_sessions_collection()453 messages_collection = await get_messages_collection()454 455 if sessions_collection and messages_collection:456 # Query anonymous sessions457 anonymous_sessions = await sessions_collection.count_documents({"user_id": None})458 assert anonymous_sessions >= 1459 460 # Query anonymous messages461 anonymous_messages = await messages_collection.count_documents({"user_id": None})462 assert anonymous_messages >= 1463 464 # Query all sessions (should include anonymous)465 all_sessions = await sessions_collection.count_documents({})466 assert all_sessions >= anonymous_sessions467 468 print("✅ Anonymous data queries work correctly")469 else:470 print("⚠️ Database not available - skipping query test")471 472 473class TestMixedDataCompatibility:474 """Test that systems work with both anonymous and authenticated data"""475 476 async def test_mixed_data_analytics(self):477 """Test analytics functions with mixed anonymous and authenticated data"""478 # Create anonymous data479 anon_session = await create_session()480 await track_message(481 session_id=anon_session.session_id,482 prompt_length=50,483 response_length=100,484 response_time_ms=1000485 )486 487 # Create authenticated data488 auth_session = await create_session(user_id="test_user")489 await track_message(490 session_id=auth_session.session_id,491 prompt_length=60,492 response_length=120,493 response_time_ms=1200,494 user_id="test_user"495 )496 497 # Test that analytics work with mixed data498 stats = await get_basic_stats()499 500 assert isinstance(stats, dict)501 assert stats["total_sessions"] >= 2502 assert stats["total_messages"] >= 2503 504 print("✅ Analytics work with mixed anonymous and authenticated data")505 506 async def test_mixed_data_queries(self):507 """Test database queries with mixed data"""508 # Create mixed data509 anon_session = await create_session()510 auth_session = await create_session(user_id="mixed_test_user")511 512 await track_message(513 session_id=anon_session.session_id,514 prompt_length=50,515 response_length=100,516 response_time_ms=1000517 )518 519 await track_message(520 session_id=auth_session.session_id,521 prompt_length=60,522 response_length=120,523 response_time_ms=1200,524 user_id="mixed_test_user"525 )526 527 # Wait for data to be written528 await asyncio.sleep(1)529 530 # Test queries531 sessions_collection = await get_sessions_collection()532 messages_collection = await get_messages_collection()533 534 if sessions_collection and messages_collection:535 # Count anonymous vs authenticated536 anonymous_sessions = await sessions_collection.count_documents({"user_id": None})537 authenticated_sessions = await sessions_collection.count_documents({"user_id": {"$ne": None}})538 total_sessions = await sessions_collection.count_documents({})539 540 assert anonymous_sessions >= 1541 assert authenticated_sessions >= 1542 assert total_sessions == anonymous_sessions + authenticated_sessions543 544 print("✅ Mixed data queries work correctly")545 else:546 print("⚠️ Database not available - skipping mixed query test")547 548 549async def run_compatibility_tests():550 """Run all backward compatibility tests"""551 print("🔄 Running Backward Compatibility Tests")552 print("=" * 50)553 554 # Test anonymous user compatibility555 anon_test = TestAnonymousUserCompatibility()556 await anon_test.test_create_session_without_user_id()557 await anon_test.test_create_session_with_none_user_id()558 await anon_test.test_track_message_without_user_id()559 await anon_test.test_track_message_with_none_user_id()560 await anon_test.test_track_search_without_user_id()561 await anon_test.test_track_search_with_none_user_id()562 print("✅ Anonymous user compatibility tests passed")563 564 # Test anonymous chat requests565 chat_test = TestAnonymousChatRequests()566 await chat_test.test_chat_request_without_user_id_field()567 await chat_test.test_chat_request_with_null_user_id()568 await chat_test.test_multiple_anonymous_requests()569 await chat_test.test_anonymous_session_continuation()570 print("✅ Anonymous chat request tests passed")571 572 # Test analytics function compatibility573 analytics_test = TestAnalyticsFunctionCompatibility()574 await analytics_test.test_basic_stats_with_anonymous_data()575 await analytics_test.test_hourly_stats_with_anonymous_data()576 await analytics_test.test_session_stats_with_anonymous_data()577 print("✅ Analytics function compatibility tests passed")578 579 # Test database compatibility580 db_test = TestDatabaseCompatibility()581 await db_test.test_anonymous_data_storage()582 await db_test.test_anonymous_data_queries()583 print("✅ Database compatibility tests passed")584 585 # Test mixed data compatibility586 mixed_test = TestMixedDataCompatibility()587 await mixed_test.test_mixed_data_analytics()588 await mixed_test.test_mixed_data_queries()589 print("✅ Mixed data compatibility tests passed")590 591 print("\n🎉 ALL BACKWARD COMPATIBILITY TESTS PASSED!")592 print("Existing anonymous user workflows continue to work as before.")593 594 595if __name__ == "__main__":596 asyncio.run(run_compatibility_tests())