File size: 17,406 Bytes
8de58aa
 
 
504a510
 
 
 
 
 
8de58aa
 
 
 
504a510
 
8de58aa
 
 
504a510
8de58aa
 
504a510
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8de58aa
504a510
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8de58aa
 
504a510
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8de58aa
504a510
 
 
 
8de58aa
504a510
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8de58aa
504a510
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
import pytest
import sys
import os
import tempfile
import json
import asyncio
from datetime import datetime, timedelta
import requests
import time

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))

from src.ai_system import SaemsTunesAISystem
from src.supabase_integration import AdvancedSupabaseIntegration
from src.security_system import AdvancedSecuritySystem
from src.monitoring_system import ComprehensiveMonitor

class TestAISystem:
    """Production AI system tests with real Supabase connections"""
    
    def setup_method(self):
        """Setup real production test fixtures"""
        # REAL SUPABASE CONNECTION - NO MOCKS
        self.supabase_url = os.environ['SUPABASE_URL']
        self.supabase_key = os.environ['SUPABASE_ANON_KEY']
        
        self.supabase = AdvancedSupabaseIntegration(
            supabase_url=self.supabase_url,
            supabase_key=self.supabase_key
        )
        
        # REAL SECURITY SYSTEM - NO MOCKS
        self.security = AdvancedSecuritySystem()
        
        # REAL MONITORING SYSTEM - NO MOCKS
        self.monitor = ComprehensiveMonitor(prometheus_port=8002)
        
        # REAL AI SYSTEM WITH PRODUCTION MODEL - NO MOCKS
        self.ai_system = SaemsTunesAISystem(
            supabase_integration=self.supabase,
            security_system=self.security,
            monitor=self.monitor,
            model_name="microsoft/Phi-3.5-mini-instruct",
            model_repo="Thetima4/Phi-3.5-mini-instruct-Q4_K_M-GGUF",
            model_file="Phi-3.5-mini-instruct-q4_k_m.gguf",
            max_response_length=300,
            temperature=0.7
        )
        
        # WAIT FOR SYSTEMS TO INITIALIZE
        time.sleep(2)
    
    def test_real_supabase_connection(self):
        """Test real Supabase connectivity with production database"""
        assert self.supabase.is_connected() == True
        
        # Test connection to actual Supabase tables
        connection_test = self.supabase.test_connection()
        assert connection_test['connected'] == True
        
        # Verify we can access real tables
        assert len(connection_test['tables']) > 0
        assert connection_test['tables']['tracks']['accessible'] == True
        assert connection_test['tables']['artists']['accessible'] == True
        assert connection_test['tables']['courses']['accessible'] == True
    
    def test_real_platform_stats_retrieval(self):
        """Test retrieving real platform statistics from production database"""
        stats = self.supabase.get_platform_stats()
        
        # Validate real data structure and values
        assert isinstance(stats, dict)
        assert 'track_count' in stats
        assert 'artist_count' in stats
        assert 'user_count' in stats
        assert 'course_count' in stats
        
        # These should be real counts from your production database
        assert stats['track_count'] >= 0
        assert stats['artist_count'] >= 0
        assert stats['user_count'] >= 0
        assert stats['course_count'] >= 0
        
        # Test that we're getting actual database counts, not fallbacks
        assert stats['track_count'] > 100  # Reasonable minimum for production
    
    def test_real_music_context_retrieval(self):
        """Test retrieving real music context from production database"""
        # Test with actual user query
        query = "guitar lessons for beginners"
        user_id = "test_user_123"
        
        context = self.supabase.get_music_context(query, user_id)
        
        # Validate real context structure
        assert isinstance(context, dict)
        assert 'tracks' in context
        assert 'artists' in context
        assert 'courses' in context
        assert 'stats' in context
        assert 'summary' in context
        
        # Verify we're getting real data arrays
        assert isinstance(context['tracks'], list)
        assert isinstance(context['artists'], list)
        assert isinstance(context['courses'], list)
        
        # Test that context is tailored to the query
        assert len(context['summary']) > 0
        assert 'guitar' in context['summary'].lower() or 'lesson' in context['summary'].lower()
    
    def test_real_popular_tracks_retrieval(self):
        """Test retrieving real popular tracks from production"""
        tracks = self.supabase.get_popular_tracks(limit=5)
        
        assert isinstance(tracks, list)
        assert len(tracks) > 0
        
        # Validate track structure with real data
        for track in tracks:
            assert 'id' in track
            assert 'title' in track
            assert 'artist' in track
            assert 'genre' in track
            assert 'plays' in track
            assert isinstance(track['title'], str)
            assert len(track['title']) > 0
            assert isinstance(track['artist'], str)
            assert len(track['artist']) > 0
    
    def test_real_popular_artists_retrieval(self):
        """Test retrieving real artists from production"""
        artists = self.supabase.get_popular_artists(limit=5)
        
        assert isinstance(artists, list)
        assert len(artists) > 0
        
        # Validate artist structure with real data
        for artist in artists:
            assert 'id' in artist
            assert 'name' in artist
            assert 'genre' in artist
            assert 'followers' in artist
            assert isinstance(artist['name'], str)
            assert len(artist['name']) > 0
    
    def test_real_courses_retrieval(self):
        """Test retrieving real courses from production"""
        courses = self.supabase.get_recent_courses(limit=5)
        
        assert isinstance(courses, list)
        assert len(courses) > 0
        
        # Validate course structure with real data
        for course in courses:
            assert 'id' in course
            assert 'title' in course
            assert 'instructor' in course
            assert 'level' in course
            assert 'students' in course
            assert isinstance(course['title'], str)
            assert len(course['title']) > 0
    
    def test_real_ai_system_health(self):
        """Test real AI system health and connectivity"""
        system_info = self.ai_system.get_system_info()
        
        assert isinstance(system_info, dict)
        assert 'model_loaded' in system_info
        assert 'model_name' in system_info
        assert 'supabase_connected' in system_info
        
        # Verify real system status
        assert system_info['supabase_connected'] == True
        assert system_info['model_name'] == "microsoft/Phi-3.5-mini-instruct"
    
    def test_real_security_system_integration(self):
        """Test real security system with actual threat detection"""
        query = "How do I create a playlist?"
        user_id = "test_user_456"
        ip_address = "192.168.1.100"
        user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        
        # Test real security check
        security_result = self.security.check_request(
            query=query,
            user_id=user_id,
            ip_address=ip_address,
            user_agent=user_agent
        )
        
        assert isinstance(security_result, dict)
        assert 'is_suspicious' in security_result
        assert 'risk_score' in security_result
        assert 'allowed' in security_result
        assert 'alerts' in security_result
        
        # Should allow legitimate requests
        assert security_result['allowed'] == True
        assert security_result['is_suspicious'] == False
        assert security_result['risk_score'] < 50
    
    def test_real_monitoring_system_integration(self):
        """Test real monitoring system with actual metrics"""
        # Record real inference metrics
        test_metrics = {
            'model_name': 'phi3.5-mini-Q4_K_M',
            'processing_time_ms': 1250.5,
            'input_tokens': 45,
            'output_tokens': 120,
            'total_tokens': 165,
            'success': True,
            'user_id': 'test_user_789',
            'conversation_id': 'conv_123',
            'timestamp': datetime.now(),
            'query_length': 25,
            'response_length': 180,
            'model_hash': 'abc123def456'
        }
        
        self.monitor.record_inference(test_metrics)
        
        # Test real performance summary
        performance_summary = self.monitor.get_performance_summary(timedelta(minutes=5))
        
        assert isinstance(performance_summary, dict)
        if performance_summary:  # Might be empty if no recent metrics
            assert 'total_requests' in performance_summary
            assert 'error_rate_percent' in performance_summary
            assert 'avg_response_time_ms' in performance_summary
    
    def test_real_system_health_monitoring(self):
        """Test real system health monitoring"""
        health_status = self.monitor.get_system_health()
        
        assert isinstance(health_status, dict)
        assert 'status' in health_status
        assert 'uptime_seconds' in health_status
        assert 'performance' in health_status
        assert 'alerts' in health_status
        
        # Should be healthy in test environment
        assert health_status['status'] in ['healthy', 'degraded', 'unhealthy']
        assert health_status['uptime_seconds'] > 0
    
    def test_real_ai_response_generation(self):
        """Test real AI response generation with production model"""
        if not self.ai_system.model_loaded:
            pytest.skip("Model not loaded, skipping response generation test")
        
        query = "How can I learn guitar on Saem's Tunes?"
        user_id = "music_learner_123"
        
        # Generate real AI response with production model
        response = self.ai_system.process_query(query, user_id)
        
        # Validate real response
        assert isinstance(response, str)
        assert len(response) > 10
        assert len(response) <= 500  # Should respect max response length
        
        # Response should be relevant to the query
        assert any(term in response.lower() for term in ['guitar', 'learn', 'course', 'lesson', 'music']) or len(response) > 0
    
    def test_real_user_context_integration(self):
        """Test real user context retrieval from production database"""
        # This would test with actual user data from your database
        # Using a test user ID that exists in your production system
        test_user_id = "existing_user_123"  # Replace with actual test user ID
        
        user_context = self.supabase.get_user_context(test_user_id)
        
        assert isinstance(user_context, dict)
        assert 'is_premium' in user_context
        assert 'favorite_genres' in user_context
        assert 'recent_activity' in user_context
        assert 'learning_progress' in user_context
        
        # These should be real user preferences from your database
        assert isinstance(user_context['is_premium'], bool)
        assert isinstance(user_context['favorite_genres'], list)
        assert isinstance(user_context['recent_activity'], list)
        assert isinstance(user_context['learning_progress'], dict)
    
    def test_real_rate_limiting(self):
        """Test real rate limiting with actual security system"""
        user_id = "rate_test_user"
        ip_address = "192.168.1.200"
        
        # Test multiple rapid requests to trigger rate limiting
        for i in range(15):
            security_result = self.security.check_request(
                query=f"Test query {i}",
                user_id=user_id,
                ip_address=ip_address,
                user_agent="Test Client"
            )
            
            if i > 10:  # After 10 requests, might hit rate limits
                # Either allowed or properly rate limited
                assert security_result['allowed'] in [True, False]
            else:
                # Should be allowed for initial requests
                assert security_result['allowed'] == True
        
        # Reset rate limits for this user/IP
        self.security.reset_rate_limits(user_id=user_id, ip_address=ip_address)
    
    def test_real_supabase_detailed_stats(self):
        """Test retrieving detailed statistics from production database"""
        detailed_stats = self.supabase.get_detailed_stats()
        
        assert isinstance(detailed_stats, dict)
        assert 'basic' in detailed_stats
        assert 'content_breakdown' in detailed_stats
        assert 'performance' in detailed_stats
        
        # Validate basic stats structure
        basic_stats = detailed_stats['basic']
        assert 'track_count' in basic_stats
        assert 'artist_count' in basic_stats
        assert 'user_count' in basic_stats
        
        # Validate content breakdown
        content_breakdown = detailed_stats['content_breakdown']
        assert 'tracks_by_popularity' in content_breakdown
        assert 'artists_by_followers' in content_breakdown
        assert 'courses_by_rating' in content_breakdown
        
        # These should be real data distributions from your database
        assert isinstance(content_breakdown['tracks_by_popularity'], list)
        assert isinstance(content_breakdown['artists_by_followers'], list)
        assert isinstance(content_breakdown['courses_by_rating'], list)
    
    def test_real_conversation_history(self):
        """Test real conversation history functionality"""
        conversation_id = "test_conv_123"
        user_id = "conv_test_user"
        
        if not self.ai_system.model_loaded:
            pytest.skip("Model not loaded, skipping conversation test")
        
        # First query
        query1 = "What music courses do you offer?"
        response1 = self.ai_system.process_query(query1, user_id, conversation_id)
        
        # Second query with conversation context
        query2 = "Which ones are good for beginners?"
        response2 = self.ai_system.process_query(query2, user_id, conversation_id)
        
        # Both should generate valid responses
        assert isinstance(response1, str)
        assert len(response1) > 0
        assert isinstance(response2, str)
        assert len(response2) > 0
        
        # Conversation history should be maintained
        assert conversation_id in self.ai_system.conversation_history
        conversation = self.ai_system.conversation_history[conversation_id]
        assert len(conversation) >= 4  # At least 2 user + 2 assistant messages
    
    def test_real_error_handling(self):
        """Test real error handling with production systems"""
        # Test with malformed query that might cause issues
        problematic_query = "A" * 10000  # Very long query
        
        security_result = self.security.check_request(
            query=problematic_query,
            user_id="error_test_user",
            ip_address="192.168.1.300"
        )
        
        # Security system should flag extremely long queries
        assert security_result['risk_score'] > 0
        assert any('long' in alert.lower() for alert in security_result['alerts'])
    
    def test_real_cache_functionality(self):
        """Test real response caching in AI system"""
        if not self.ai_system.model_loaded:
            pytest.skip("Model not loaded, skipping cache test")
        
        query = "How do I upload my music to Saem's Tunes?"
        user_id = "cache_test_user"
        
        # First request - should process normally
        start_time = time.time()
        response1 = self.ai_system.process_query(query, user_id)
        first_duration = time.time() - start_time
        
        # Second request - should be faster if cached
        start_time = time.time()
        response2 = self.ai_system.process_query(query, user_id)
        second_duration = time.time() - start_time
        
        # Responses should be identical
        assert response1 == response2
        
        # Second request should be faster (cached)
        # Note: This might not always hold true in production, but generally should
        assert second_duration <= first_duration * 0.5  # Should be at least 50% faster
    
    def test_real_system_cleanup(self):
        """Test real system cleanup and resource management"""
        # Test cache clearing
        initial_cache_size = len(self.ai_system.response_cache)
        self.ai_system.clear_cache()
        final_cache_size = len(self.ai_system.response_cache)
        
        assert final_cache_size == 0
        assert final_cache_size < initial_cache_size or initial_cache_size == 0
        
        # Test security system cleanup
        initial_security_log_size = len(self.security.security_log)
        self.security.security_log = []  # Clear security log
        assert len(self.security.security_log) == 0
        
        # Test monitoring system reset
        self.monitor.reset_metrics()
        recent_metrics = self.monitor.get_recent_metrics(minutes=1)
        assert len(recent_metrics) == 0

    def teardown_method(self):
        """Cleanup after tests"""
        if hasattr(self, 'monitor'):
            self.monitor.stop_monitoring()
        if hasattr(self, 'ai_system'):
            self.ai_system.clear_cache()