import 'package:flutter_test/flutter_test.dart'; import '../lib/core/services/vector_service.dart'; import '../lib/core/services/rag_service.dart'; import '../lib/core/services/rag_ai_service.dart'; import '../lib/core/models/content_chunk.dart'; void main() { group('AI Tutor RAG Services Tests', () { test('VectorService - Generate Embedding', () async { print('🔍 Testing VectorService embedding generation...'); const testText = 'A fotossíntese é o processo pelo qual as plantas convertem luz solar em energia química.'; final embedding = VectorService.generateEmbedding(testText); // Verify embedding properties expect(embedding.length, equals(384)); // Standard embedding size expect(embedding.every((x) => x >= -1.0 && x <= 1.0), isTrue); // Normalized print('✅ Embedding generated successfully: ${embedding.length} dimensions'); }); test('VectorService - Cosine Similarity', () { print('🔍 Testing cosine similarity calculation...'); const text1 = 'A fotossíntese é importante para as plantas.'; const text2 = 'As plantas usam fotossíntese para produzir energia.'; const text3 = 'Os animais precisam de comida para sobreviver.'; final embedding1 = VectorService.generateEmbedding(text1); final embedding2 = VectorService.generateEmbedding(text2); final embedding3 = VectorService.generateEmbedding(text3); final similarity12 = VectorService.cosineSimilarity(embedding1, embedding2); final similarity13 = VectorService.cosineSimilarity(embedding1, embedding3); // Similar texts should have higher similarity expect(similarity12, greaterThan(similarity13)); expect(similarity12, greaterThan(0.5)); // Should be reasonably similar expect(similarity13, lessThan(0.3)); // Should be less similar print('✅ Cosine similarity working correctly'); print(' Similar (fotossíntese): ${similarity12.toStringAsFixed(3)}'); print(' Different (plantas vs animais): ${similarity13.toStringAsFixed(3)}'); }); test('VectorService - Search by Text', () async { print('🔍 Testing vector search by text...'); // This would normally search the database, but we'll test the embedding part const query = 'O que é fotossíntese?'; final results = await VectorService.searchByText( query: query, subject: 'Biologia', concept: 'Fotossíntese', grade: 10, k: 3, ); // Results might be empty if no data in database, but the call should work expect(results, isA>()); print('✅ Vector search completed'); print(' Found ${results.length} results for: "$query"'); }); test('RAGAIService - Service Availability', () async { print('🔍 Testing Ollama service availability...'); try { final isAvailable = await RAGAIService.isServiceAvailable(); if (isAvailable) { print('✅ Ollama service is available'); // Test model info final modelInfo = await RAGAIService.getModelInfo(); if (modelInfo != null) { print(' Model: ${modelInfo['name']}'); print(' Size: ${modelInfo['size']}'); print(' Modified: ${modelInfo['modified']}'); } // Test simple query final testResponse = await RAGAIService.testService(); print(' Test response: "$testResponse"'); } else { print('⚠️ Ollama service is not available'); print(' This is expected if the service is not running'); } } catch (e) { print('❌ Error testing service availability: $e'); } }); test('RAG Pipeline - End-to-End Simulation', () async { print('🔍 Testing complete RAG pipeline simulation...'); try { const userQuery = 'O que é fotossíntese?'; const mode = TutorMode.explanation; // Step 1: Test query embedding final queryEmbedding = VectorService.generateEmbedding(userQuery); print(' ✅ Query embedding generated (${queryEmbedding.length} dims)'); // Step 2: Test vector search final searchResults = await VectorService.searchSimilar( queryEmbedding: queryEmbedding, subject: 'Biologia', concept: 'Fotossíntese', grade: 10, k: 3, ); print(' ✅ Vector search completed (${searchResults.length} results)'); // Step 3: Test RAG processing (with mock data if no real data) if (searchResults.isEmpty) { print(' ⚠️ No content found, creating mock data for testing...'); // Create mock chunks for testing final mockChunks = [ ContentChunk( id: 'mock1', contentId: 'mock_content1', text: 'A fotossíntese é o processo pelo qual as plantas convertem luz solar em energia química.', subject: 'Biologia', concept: 'Fotossíntese', unit: 'Processos Biológicos', difficulty: 0.6, grade: 10, embedding: VectorService.generateEmbedding('fotossíntese plantas energia'), sourceDocument: 'Biologia Manual.pdf', metadata: {'page': 45}, createdAt: DateTime.now(), ), ]; // Test RAG processing with mock data final ragResponse = await RAGService.processQuery( userQuery: userQuery, mode: mode, subject: 'Biologia', concept: 'Fotossíntese', grade: 10, maxSources: 3, ); print(' ✅ RAG processing completed'); print(' Answer: "${ragResponse.answer.substring(0, 100)}..."'); print(' Confidence: ${ragResponse.confidence.toStringAsFixed(2)}'); print(' Sources: ${ragResponse.sources.length}'); print(' Related concepts: ${ragResponse.relatedConcepts.join(', ')}'); } else { print(' ✅ Found real content for RAG processing'); // Test with real data final ragResponse = await RAGService.processQuery( userQuery: userQuery, mode: mode, subject: 'Biologia', concept: 'Fotossíntese', grade: 10, maxSources: 3, ); print(' ✅ RAG processing with real data completed'); print(' Answer: "${ragResponse.answer.substring(0, 100)}..."'); print(' Confidence: ${ragResponse.confidence.toStringAsFixed(2)}'); print(' Sources: ${ragResponse.sources.length}'); } print('✅ RAG pipeline simulation completed successfully'); } catch (e) { print('❌ Error in RAG pipeline: $e'); } }); test('VectorService - Batch Embeddings', () async { print('🔍 Testing batch embedding generation...'); final texts = [ 'A fotossíntese converte luz solar em energia.', 'As plantas usam clorofila para capturar luz.', 'O oxigénio é um subproduto da fotossíntese.', ]; final embeddings = await VectorService.batchGenerateEmbeddings(texts); expect(embeddings.length, equals(texts.length)); expect(embeddings.every((e) => e.length == 384), isTrue); print('✅ Batch embeddings generated successfully'); print(' Generated ${embeddings.length} embeddings'); // Test similarities between batch embeddings for (int i = 0; i < embeddings.length; i++) { for (int j = i + 1; j < embeddings.length; j++) { final similarity = VectorService.cosineSimilarity(embeddings[i], embeddings[j]); print(' Similarity ${i+1}-${j+1}: ${similarity.toStringAsFixed(3)}'); } } }); test('VectorService - Statistics', () async { print('🔍 Testing vector statistics...'); final stats = await VectorService.getVectorStats(); expect(stats, isA>()); expect(stats.containsKey('totalChunks'), isTrue); expect(stats.containsKey('embeddingDimension'), isTrue); expect(stats['embeddingDimension'], equals(384)); print('✅ Vector statistics retrieved'); print(' Total chunks: ${stats['totalChunks']}'); print(' Embedding dimension: ${stats['embeddingDimension']}'); print(' Subjects: ${stats['subjects'].keys.length}'); print(' Concepts: ${stats['concepts'].keys.length}'); }); }); }