Skip to main content
Glama
test-image-selection-integration.js22 kB
/** * Integration Test: Context-Aware Image Selection System * Task 3.2: Context-Aware Image Selection Validation * * Tests the complete image selection and mapping system: * - Subject-specific image selection * - Widget-specific image mapping * - Educational context awareness * - Fallback mechanisms * - Integration with Phase 2 assessment components */ const { ImageSelectionService } = require('../../src/services/image-selection-service'); const { EducationalImageMapper } = require('../../src/widget-mappers/educational-image-mapper'); class ImageSelectionIntegrationTest { constructor() { this.imageService = new ImageSelectionService(); this.imageMapper = new EducationalImageMapper(); this.testResults = { total: 0, passed: 0, failed: 0, details: [] }; } /** * Run comprehensive integration tests */ async runIntegrationTests() { console.log('🧪 Starting Image Selection Integration Tests...\n'); // Test 1: Subject-Specific Image Selection await this.testSubjectSpecificSelection(); // Test 2: Widget-Specific Image Mapping await this.testWidgetSpecificMapping(); // Test 3: Educational Context Awareness await this.testEducationalContextAwareness(); // Test 4: Fallback Mechanisms await this.testFallbackMechanisms(); // Test 5: Phase 2 Assessment Integration await this.testAssessmentComponentIntegration(); // Test 6: Brazilian Educational Standards await this.testBrazilianEducationalStandards(); // Test 7: Performance and Quality Metrics await this.testPerformanceAndQuality(); this.printResults(); return this.testResults; } /** * Test 1: Subject-Specific Image Selection */ async testSubjectSpecificSelection() { console.log('📋 Test 1: Subject-Specific Image Selection'); const testCases = [ { name: 'Physics - Ballistics', content: 'O movimento de projéteis segue uma trajetória parabólica devido à ação da gravidade', subject: 'física', expectedPattern: 'trajetoria' }, { name: 'Chemistry - Molecular Structure', content: 'As moléculas de água possuem ligações covalentes entre átomos de hidrogênio e oxigênio', subject: 'química', expectedPattern: 'moleculas' }, { name: 'History - Brazilian Independence', content: 'A independência do Brasil foi proclamada por Dom Pedro I em 1822', subject: 'história', expectedPattern: 'independencia' }, { name: 'Biology - Photosynthesis', content: 'A fotossíntese ocorre nos cloroplastos das células vegetais', subject: 'ciências', expectedPattern: 'fotossintese' } ]; for (const testCase of testCases) { this.testResults.total++; try { const result = this.imageService.selectContextualImage( testCase.content, testCase.subject, 'fundamental', 'image-1' ); // Validate result structure const hasRequiredFields = result.url && result.caption && result.alt_text && result.educational_context; const hasSubjectContext = result.educational_context.includes(testCase.subject); const isEducationalUrl = result.url.includes('education') || result.url.includes('cdn.digitalpages'); if (hasRequiredFields && hasSubjectContext && isEducationalUrl) { this.testResults.passed++; this.testResults.details.push({ test: testCase.name, status: 'PASSED', message: `Correctly selected ${testCase.subject} image`, data: { url: result.url, context: result.educational_context } }); console.log(` ✅ ${testCase.name}: PASSED`); } else { throw new Error(`Invalid image selection result for ${testCase.subject}`); } } catch (error) { this.testResults.failed++; this.testResults.details.push({ test: testCase.name, status: 'FAILED', message: error.message, data: null }); console.log(` ❌ ${testCase.name}: FAILED - ${error.message}`); } } console.log(''); } /** * Test 2: Widget-Specific Image Mapping */ async testWidgetSpecificMapping() { console.log('📋 Test 2: Widget-Specific Image Mapping'); const testCases = [ { name: 'Header Widget (head-1)', context: { content: 'Aula sobre fotossíntese', subject: 'ciências', grade_level: 'fundamental', widget_type: 'head-1', learning_intent: 'introduction' }, expectedProperties: ['background_image', 'avatar'] }, { name: 'Quiz Widget (quiz-1)', context: { content: 'Questões sobre movimento de projéteis', subject: 'física', grade_level: 'médio', widget_type: 'quiz-1', learning_intent: 'assessment' }, expectedProperties: ['question_images', 'feedback_correct', 'feedback_incorrect'] }, { name: 'Flashcard Widget (flashcards-1)', context: { content: 'Conceitos de química molecular', subject: 'química', grade_level: 'médio', widget_type: 'flashcards-1', learning_intent: 'memorization' }, expectedProperties: ['card_images'] }, { name: 'Gallery Widget (gallery-1)', context: { content: 'Processo histórico da independência brasileira', subject: 'história', grade_level: 'fundamental', widget_type: 'gallery-1', learning_intent: 'exploration' }, expectedProperties: ['slides'] } ]; for (const testCase of testCases) { this.testResults.total++; try { const result = this.imageMapper.mapContentToImages(testCase.context); // Validate widget mapping const hasCorrectWidgetType = result.widget_type === testCase.context.widget_type; const hasImageProperties = result.image_properties && Object.keys(result.image_properties).length > 0; const hasEducationalMetadata = result.educational_metadata && result.educational_metadata.subject === testCase.context.subject; // Check for expected properties const hasExpectedProperties = testCase.expectedProperties.some(prop => result.image_properties.hasOwnProperty(prop) ); if (hasCorrectWidgetType && hasImageProperties && hasEducationalMetadata && hasExpectedProperties) { this.testResults.passed++; this.testResults.details.push({ test: testCase.name, status: 'PASSED', message: `Correctly mapped images for ${testCase.context.widget_type}`, data: { widget_type: result.widget_type, properties: Object.keys(result.image_properties), metadata: result.educational_metadata } }); console.log(` ✅ ${testCase.name}: PASSED`); } else { throw new Error(`Invalid widget mapping for ${testCase.context.widget_type}`); } } catch (error) { this.testResults.failed++; this.testResults.details.push({ test: testCase.name, status: 'FAILED', message: error.message, data: null }); console.log(` ❌ ${testCase.name}: FAILED - ${error.message}`); } } console.log(''); } /** * Test 3: Educational Context Awareness */ async testEducationalContextAwareness() { console.log('📋 Test 3: Educational Context Awareness'); const testCases = [ { name: 'Grade Level Adaptation - Elementary', content: 'Plantas fazem fotossíntese', subject: 'ciências', grade_level: 'fundamental', expectedComplexity: 'basic' }, { name: 'Grade Level Adaptation - High School', content: 'O processo fotossintético envolve reações químicas complexas nos tilacoides', subject: 'ciências', grade_level: 'médio', expectedComplexity: 'advanced' }, { name: 'Learning Intent - Assessment', content: 'Questão sobre forças e movimento', subject: 'física', grade_level: 'médio', learning_intent: 'assessment', expectedImageType: 'quiz' }, { name: 'Learning Intent - Exploration', content: 'Explore as diferentes fases da mitose', subject: 'ciências', grade_level: 'médio', learning_intent: 'exploration', expectedImageType: 'interactive' } ]; for (const testCase of testCases) { this.testResults.total++; try { const result = this.imageService.selectContextualImage( testCase.content, testCase.subject, testCase.grade_level, 'image-1' ); // Validate educational context awareness const hasGradeLevelContext = result.educational_context.includes(testCase.grade_level); const hasSubjectContext = result.educational_context.includes(testCase.subject); const hasAppropriateCaption = result.caption.length > 10; // Reasonable caption length const hasAccessibleAltText = result.alt_text.length > 5; // Accessible alt text if (hasGradeLevelContext && hasSubjectContext && hasAppropriateCaption && hasAccessibleAltText) { this.testResults.passed++; this.testResults.details.push({ test: testCase.name, status: 'PASSED', message: `Context-aware image selection successful`, data: { context: result.educational_context, caption: result.caption, alt_text: result.alt_text } }); console.log(` ✅ ${testCase.name}: PASSED`); } else { throw new Error(`Insufficient educational context awareness`); } } catch (error) { this.testResults.failed++; this.testResults.details.push({ test: testCase.name, status: 'FAILED', message: error.message, data: null }); console.log(` ❌ ${testCase.name}: FAILED - ${error.message}`); } } console.log(''); } /** * Test 4: Fallback Mechanisms */ async testFallbackMechanisms() { console.log('📋 Test 4: Fallback Mechanisms'); const testCases = [ { name: 'Unknown Subject Fallback', content: 'Conteúdo sobre assunto desconhecido', subject: 'assunto_inexistente', expectedFallback: true }, { name: 'Unknown Topic Fallback', content: 'Tópico completamente novo e desconhecido', subject: 'física', expectedFallback: true }, { name: 'Empty Content Fallback', content: '', subject: 'matemática', expectedFallback: true }, { name: 'Invalid Grade Level Fallback', content: 'Conteúdo educacional', subject: 'história', grade_level: 'nivel_inexistente', expectedFallback: false } ]; for (const testCase of testCases) { this.testResults.total++; try { const result = this.imageService.selectContextualImage( testCase.content, testCase.subject, testCase.grade_level || 'fundamental', 'image-1' ); // Validate fallback mechanism const isFallbackUsed = result.fallback_used === true; const hasValidUrl = result.url && result.url.startsWith('http'); const hasValidCaption = result.caption && result.caption.length > 0; const hasValidContext = result.educational_context && result.educational_context.length > 0; // Check if fallback expectation matches result const fallbackExpectationMet = testCase.expectedFallback === isFallbackUsed; if (hasValidUrl && hasValidCaption && hasValidContext && (testCase.expectedFallback ? isFallbackUsed : true)) { this.testResults.passed++; this.testResults.details.push({ test: testCase.name, status: 'PASSED', message: `Fallback mechanism working correctly`, data: { fallback_used: result.fallback_used, url: result.url, context: result.educational_context } }); console.log(` ✅ ${testCase.name}: PASSED`); } else { throw new Error(`Fallback mechanism failed`); } } catch (error) { this.testResults.failed++; this.testResults.details.push({ test: testCase.name, status: 'FAILED', message: error.message, data: null }); console.log(` ❌ ${testCase.name}: FAILED - ${error.message}`); } } console.log(''); } /** * Test 5: Phase 2 Assessment Integration */ async testAssessmentComponentIntegration() { console.log('📋 Test 5: Phase 2 Assessment Component Integration'); const mockAssessmentData = { quiz: { metadata: { topic: 'Movimento de Projéteis', gradeLevel: 'médio', subject: 'física' }, questions: [ { id: 1, question: 'Qual a trajetória de um projétil?', type: 'multiple_choice' }, { id: 2, question: 'Como calcular o alcance máximo?', type: 'calculation' } ] }, flashcards: [ 'Trajetória parabólica', 'Velocidade inicial', 'Ângulo de lançamento', 'Gravidade terrestre' ], metadata: { gradeLevel: 'médio', subject: 'física' } }; this.testResults.total++; try { const mappings = this.imageMapper.integrateWithAssessmentEngine( mockAssessmentData, 'física' ); // Validate assessment integration const hasQuizMapping = mappings.some(m => m.widget_type === 'quiz-1'); const hasFlashcardMapping = mappings.some(m => m.widget_type === 'flashcards-1'); const allMappingsValid = mappings.every(m => m.image_properties && m.educational_metadata && m.educational_metadata.subject === 'física' ); if (hasQuizMapping && hasFlashcardMapping && allMappingsValid) { this.testResults.passed++; this.testResults.details.push({ test: 'Assessment Integration', status: 'PASSED', message: `Successfully integrated with Phase 2 assessment components`, data: { mappings_count: mappings.length, widget_types: mappings.map(m => m.widget_type) } }); console.log(` ✅ Assessment Integration: PASSED`); } else { throw new Error(`Failed to integrate with assessment components`); } } catch (error) { this.testResults.failed++; this.testResults.details.push({ test: 'Assessment Integration', status: 'FAILED', message: error.message, data: null }); console.log(` ❌ Assessment Integration: FAILED - ${error.message}`); } console.log(''); } /** * Test 6: Brazilian Educational Standards */ async testBrazilianEducationalStandards() { console.log('📋 Test 6: Brazilian Educational Standards (BNCC)'); const brazilianTestCases = [ { name: 'Portuguese Language Content', content: 'Análise de obra literária brasileira: Machado de Assis', subject: 'português', grade_level: 'médio' }, { name: 'Brazilian History Content', content: 'A colonização portuguesa no Brasil e suas consequências', subject: 'história', grade_level: 'fundamental' }, { name: 'Regional Science Content', content: 'A biodiversidade da Amazônia brasileira', subject: 'ciências', grade_level: 'fundamental' } ]; for (const testCase of brazilianTestCases) { this.testResults.total++; try { const result = this.imageService.selectContextualImage( testCase.content, testCase.subject, testCase.grade_level, 'image-1' ); // Validate Brazilian educational context const hasBrazilianContext = result.caption.includes('Brasil') || result.caption.includes('brasileiro') || result.educational_context.includes(testCase.subject); const hasPortugueseCaption = /[áàâãéêíóôõúç]/.test(result.caption); // Check for Portuguese characters const hasEducationalStructure = result.educational_context.split('/').length >= 3; if (hasBrazilianContext && hasPortugueseCaption && hasEducationalStructure) { this.testResults.passed++; this.testResults.details.push({ test: testCase.name, status: 'PASSED', message: `BNCC compliance validated`, data: { caption: result.caption, context: result.educational_context } }); console.log(` ✅ ${testCase.name}: PASSED`); } else { throw new Error(`BNCC compliance issues detected`); } } catch (error) { this.testResults.failed++; this.testResults.details.push({ test: testCase.name, status: 'FAILED', message: error.message, data: null }); console.log(` ❌ ${testCase.name}: FAILED - ${error.message}`); } } console.log(''); } /** * Test 7: Performance and Quality Metrics */ async testPerformanceAndQuality() { console.log('📋 Test 7: Performance and Quality Metrics'); const performanceTests = [ { name: 'Image Selection Speed', test: async () => { const startTime = Date.now(); await this.imageService.selectContextualImage( 'Teste de performance para seleção de imagens', 'física', 'médio', 'image-1' ); const endTime = Date.now(); return endTime - startTime; }, threshold: 2000, // 2 seconds max metric: 'milliseconds' }, { name: 'Widget Mapping Speed', test: async () => { const startTime = Date.now(); await this.imageMapper.mapContentToImages({ content: 'Teste de performance para mapeamento de widgets', subject: 'química', grade_level: 'médio', widget_type: 'quiz-1', learning_intent: 'assessment' }); const endTime = Date.now(); return endTime - startTime; }, threshold: 1000, // 1 second max metric: 'milliseconds' } ]; for (const perfTest of performanceTests) { this.testResults.total++; try { const duration = await perfTest.test(); if (duration <= perfTest.threshold) { this.testResults.passed++; this.testResults.details.push({ test: perfTest.name, status: 'PASSED', message: `Performance within threshold: ${duration}${perfTest.metric}`, data: { duration, threshold: perfTest.threshold } }); console.log(` ✅ ${perfTest.name}: PASSED (${duration}ms)`); } else { throw new Error(`Performance threshold exceeded: ${duration}ms > ${perfTest.threshold}ms`); } } catch (error) { this.testResults.failed++; this.testResults.details.push({ test: perfTest.name, status: 'FAILED', message: error.message, data: null }); console.log(` ❌ ${perfTest.name}: FAILED - ${error.message}`); } } console.log(''); } /** * Print test results summary */ printResults() { console.log('📊 IMAGE SELECTION INTEGRATION TEST RESULTS'); console.log('=' .repeat(50)); console.log(`Total Tests: ${this.testResults.total}`); console.log(`Passed: ${this.testResults.passed} ✅`); console.log(`Failed: ${this.testResults.failed} ❌`); console.log(`Success Rate: ${((this.testResults.passed / this.testResults.total) * 100).toFixed(1)}%`); console.log(''); if (this.testResults.failed > 0) { console.log('❌ FAILED TESTS:'); this.testResults.details .filter(detail => detail.status === 'FAILED') .forEach(detail => { console.log(` - ${detail.test}: ${detail.message}`); }); console.log(''); } const successRate = (this.testResults.passed / this.testResults.total) * 100; if (successRate >= 90) { console.log('🎉 EXCELLENT: Image selection system ready for production!'); } else if (successRate >= 75) { console.log('✅ GOOD: Image selection system functional with minor issues'); } else { console.log('⚠️ WARNING: Image selection system needs significant improvements'); } } } // Run tests if called directly if (require.main === module) { const tester = new ImageSelectionIntegrationTest(); tester.runIntegrationTests() .then(results => { process.exit(results.failed > 0 ? 1 : 0); }) .catch(error => { console.error('Test execution failed:', error); process.exit(1); }); } module.exports = { ImageSelectionIntegrationTest };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rkm097git/euconquisto-composer-mcp-poc'

If you have feedback or need assistance with the MCP directory API, please join our Discord server