Skip to main content
Glama
workflow-integration-test.js16.2 kB
#!/usr/bin/env node /** * Multi-Tool Workflow Integration Test * Tests the complete workflow from lesson data through to Composer JSON * @version 1.0.0 */ import { createLessonDataValidator } from '../src/tools/validate-lesson-data.js'; import { createComposerFormatter } from '../src/tools/format-for-composer.js'; // Sample lesson data (similar to what Claude would generate) const claudeLessonData = { metadata: { topic: "Geometria - Área e Perímetro", duration: 50, learningObjectives: [ "Calcular área de figuras geométricas básicas", "Determinar perímetro de polígonos", "Aplicar fórmulas geométricas em problemas práticos", "Distinguir entre área e perímetro" ], bnccAlignment: "EF06MA29, EF06MA30", subject: "Matemática", gradeLevel: "6º ano" }, widgets: [ { type: "head-1", content: { category: "Matemática - 6º ano", author_name: "Professor de Matemática", author_office: "Especialista em Geometria", background_image: "https://images.unsplash.com/photo-1635070041078-e363dbe005cb?w=1200&h=400&fit=crop" }, rationale: "Professional mathematical presentation with geometric theme", educationalGoal: "Establish credibility and mathematical context" }, { type: "text-1", content: { text: "<h2>🔢 Geometria na Vida Real</h2><p>Você sabia que usamos geometria todos os dias? Quando calculamos quantos metros de fita precisamos para contornar um presente (perímetro) ou quantos metros quadrados de tinta são necessários para pintar uma parede (área), estamos aplicando conceitos geométricos!</p><h3>Conceitos Importantes:</h3><p><strong>Perímetro:</strong> A medida do contorno de uma figura geométrica.</p><p><strong>Área:</strong> A medida da superfície dentro de uma figura geométrica.</p>", title: "Introdução: Geometria no Cotidiano", background_color: "#FEF2F2" }, rationale: "Contextual introduction connecting geometry to daily life", educationalGoal: "Motivate learning through real-world connections" }, { type: "image-1", content: { image: "https://images.unsplash.com/photo-1518133835878-5a93cc3f89e5?w=800&h=600&fit=crop", caption: "Figuras geométricas estão presentes em construções, arte e natureza", title: "Geometria ao Nosso Redor" }, rationale: "Visual reinforcement of geometric concepts in real world", educationalGoal: "Visual learning support for abstract concepts" }, { type: "text-1", content: { text: "<h2>📏 Fórmulas Essenciais</h2><div style='background-color: #F0FDF4; padding: 20px; border-radius: 10px; margin: 20px 0;'><h3>Retângulo</h3><p><strong>Perímetro:</strong> P = 2 × (comprimento + largura)</p><p><strong>Área:</strong> A = comprimento × largura</p></div><div style='background-color: #EFF6FF; padding: 20px; border-radius: 10px; margin: 20px 0;'><h3>Quadrado</h3><p><strong>Perímetro:</strong> P = 4 × lado</p><p><strong>Área:</strong> A = lado × lado</p></div><div style='background-color: #FEF7CD; padding: 20px; border-radius: 10px; margin: 20px 0;'><h3>Triângulo</h3><p><strong>Perímetro:</strong> P = lado₁ + lado₂ + lado₃</p><p><strong>Área:</strong> A = (base × altura) ÷ 2</p></div>", title: "Fórmulas Fundamentais", background_color: "#FFFFFF" }, rationale: "Clear presentation of mathematical formulas with color coding", educationalGoal: "Provide reference formulas for practical application" }, { type: "flashcards-1", content: { title: "Revisão de Conceitos", flashcards_items: [ { question: "O que é perímetro?", answer: "É a medida do contorno de uma figura geométrica. Somamos todos os lados." }, { question: "O que é área?", answer: "É a medida da superfície dentro de uma figura geométrica. Calculamos com fórmulas específicas." }, { question: "Fórmula da área do retângulo", answer: "Área = comprimento × largura" }, { question: "Fórmula do perímetro do quadrado", answer: "Perímetro = 4 × lado" }, { question: "Unidade de medida da área", answer: "Metros quadrados (m²), centímetros quadrados (cm²), etc." } ] }, rationale: "Active recall practice for key geometric concepts", educationalGoal: "Reinforce vocabulary and formula memorization" }, { type: "quiz-1", content: { title: "Avaliação: Área e Perímetro", questions: [ { question: "Um retângulo tem 8 metros de comprimento e 5 metros de largura. Qual é o seu perímetro?", options: ["13 metros", "26 metros", "40 metros", "20 metros"], correct_option: 1, feedback: { correct: "Perfeito! P = 2 × (8 + 5) = 2 × 13 = 26 metros", incorrect: "Lembre-se: Perímetro do retângulo = 2 × (comprimento + largura)" } }, { question: "Qual é a área de um quadrado com lado de 6 cm?", options: [ { text: "24 cm²", correct: false }, { text: "36 cm²", correct: true }, { text: "12 cm²", correct: false }, { text: "18 cm²", correct: false } ], feedback: { correct: "Excelente! Área = lado × lado = 6 × 6 = 36 cm²", incorrect: "Para o quadrado: Área = lado × lado" } }, { question: "Uma sala tem 4m de largura e 6m de comprimento. Quantos metros quadrados de piso serão necessários?", options: ["10 m²", "20 m²", "24 m²", "40 m²"], correct_option: 2, feedback: { correct: "Correto! Área = 4 × 6 = 24 m²", incorrect: "Para calcular a área do piso: comprimento × largura" } }, { question: "Qual a diferença entre área e perímetro?", options: [ { text: "Área é o contorno, perímetro é a superfície", correct: false }, { text: "Perímetro é o contorno, área é a superfície", correct: true }, { text: "São a mesma coisa", correct: false }, { text: "Área é sempre maior que perímetro", correct: false } ] } ], max_attempts: 3 }, rationale: "Comprehensive assessment covering calculation and conceptual understanding", educationalGoal: "Evaluate mastery of geometric calculations and concepts" } ], assessmentStrategy: "Combination of flashcards for vocabulary review and quiz for problem-solving assessment", widgetSelectionSummary: "Progressive learning: introduction → formulas → practice → assessment, with visual and interactive elements for engagement" }; class WorkflowIntegrationTester { constructor() { this.validator = createLessonDataValidator(); this.formatter = createComposerFormatter(); this.workflowSteps = []; } async runWorkflowTest() { console.error('\n=== MULTI-TOOL WORKFLOW INTEGRATION TEST ===\n'); console.error('Testing complete workflow: Lesson Data → Validation → Formatting → Composer JSON\n'); try { // Step 1: Validate lesson data await this.stepValidation(); // Step 2: Format for Composer await this.stepFormatting(); // Step 3: Validate final output await this.stepOutputValidation(); // Summary this.printWorkflowSummary(); } catch (error) { console.error('❌ WORKFLOW FAILED:', error.message); this.printWorkflowSummary(); } } async stepValidation() { console.error('📋 STEP 1: Lesson Data Validation'); console.error('Input: Claude-generated lesson data for Geometry (6º ano)'); const startTime = Date.now(); const validationResult = await this.validator.validateLessonData(claudeLessonData); const validationTime = Date.now() - startTime; if (validationResult.success) { console.error(`✅ Validation successful (${validationTime}ms)`); console.error(` Widgets validated: ${validationResult.data.widgetCount}`); console.error(` Validation checks: ${validationResult.debug.validationChecks.length}`); console.error(` Summary: ${validationResult.data.validationSummary}`); this.workflowSteps.push({ step: 'Validation', success: true, time: validationTime, output: validationResult.data.validatedLessonData }); } else { console.error(`❌ Validation failed (${validationTime}ms)`); console.error(` Errors: ${validationResult.error.details.length}`); validationResult.error.details.slice(0, 3).forEach(error => { console.error(` - ${error.type}: ${error.field} in ${error.location}`); }); this.workflowSteps.push({ step: 'Validation', success: false, time: validationTime, error: validationResult.error }); throw new Error('Validation step failed'); } console.error(''); } async stepFormatting() { console.error('🎨 STEP 2: Composer Formatting'); console.error('Input: Validated lesson data'); const validatedData = this.workflowSteps[0].output; const startTime = Date.now(); const formatResult = await this.formatter.formatForComposer(validatedData); const formatTime = Date.now() - startTime; if (formatResult.success) { console.error(`✅ Formatting successful (${formatTime}ms)`); console.error(` Widgets formatted: ${formatResult.data.widgetCount}`); console.error(` Educational optimizations: ${formatResult.data.educationalOptimizations.length}`); console.error(` Assets extracted: ${formatResult.data.composerJSON.assets.length}`); console.error(` Summary: ${formatResult.data.formatSummary}`); this.workflowSteps.push({ step: 'Formatting', success: true, time: formatTime, output: formatResult.data.composerJSON }); } else { console.error(`❌ Formatting failed (${formatTime}ms)`); console.error(` Error: ${formatResult.error.message}`); this.workflowSteps.push({ step: 'Formatting', success: false, time: formatTime, error: formatResult.error }); throw new Error('Formatting step failed'); } console.error(''); } async stepOutputValidation() { console.error('🔍 STEP 3: Output Validation'); console.error('Validating final Composer JSON structure'); const composerJSON = this.workflowSteps[1].output; const validationResults = []; // Check required top-level properties const requiredProps = ['uid', 'metadata', 'structure', 'assets']; requiredProps.forEach(prop => { const hasProperty = composerJSON.hasOwnProperty(prop); validationResults.push({ check: `Has ${prop} property`, passed: hasProperty }); if (hasProperty) { console.error(`✅ Has ${prop} property`); } else { console.error(`❌ Missing ${prop} property`); } }); // Check metadata structure if (composerJSON.metadata) { const metadataProps = ['uid', 'name', 'title', 'description', 'subject', 'segment']; metadataProps.forEach(prop => { const hasProperty = composerJSON.metadata.hasOwnProperty(prop); validationResults.push({ check: `Metadata has ${prop}`, passed: hasProperty }); }); console.error(`✅ Metadata structure complete (${metadataProps.length} properties)`); } // Check widgets structure if (composerJSON.structure && Array.isArray(composerJSON.structure)) { console.error(`✅ Structure is array with ${composerJSON.structure.length} widgets`); // Check first widget is head-1 const firstWidget = composerJSON.structure[0]; if (firstWidget && firstWidget.type === 'head-1') { console.error('✅ First widget is head-1 (professional presentation)'); validationResults.push({ check: 'First widget is head-1', passed: true }); } else { console.error('❌ First widget is not head-1'); validationResults.push({ check: 'First widget is head-1', passed: false }); } // Check widget IDs and types let allWidgetsValid = true; composerJSON.structure.forEach((widget, index) => { if (!widget.id || !widget.type) { console.error(`❌ Widget ${index + 1} missing id or type`); allWidgetsValid = false; } }); if (allWidgetsValid) { console.error('✅ All widgets have required id and type properties'); validationResults.push({ check: 'All widgets valid', passed: true }); } else { validationResults.push({ check: 'All widgets valid', passed: false }); } } // Check specific widget types are present const widgetTypes = composerJSON.structure.map(w => w.type); const expectedTypes = ['head-1', 'text-1', 'quiz-1', 'flashcards-1']; expectedTypes.forEach(expectedType => { const hasType = widgetTypes.includes(expectedType); if (hasType) { console.error(`✅ Contains ${expectedType} widget`); } else { console.error(`❌ Missing ${expectedType} widget`); } validationResults.push({ check: `Contains ${expectedType}`, passed: hasType }); }); const passedChecks = validationResults.filter(r => r.passed).length; const totalChecks = validationResults.length; const successRate = Math.round((passedChecks / totalChecks) * 100); console.error(`\n📊 Output validation: ${passedChecks}/${totalChecks} checks passed (${successRate}%)`); this.workflowSteps.push({ step: 'Output Validation', success: successRate >= 90, checks: validationResults, successRate: successRate }); console.error(''); } printWorkflowSummary() { console.error('=== WORKFLOW SUMMARY ===\n'); const totalTime = this.workflowSteps .filter(step => step.time) .reduce((sum, step) => sum + step.time, 0); const allStepsSuccessful = this.workflowSteps.every(step => step.success); console.error('Workflow Steps:'); this.workflowSteps.forEach((step, index) => { const status = step.success ? '✅' : '❌'; const time = step.time ? `(${step.time}ms)` : ''; console.error(`${index + 1}. ${status} ${step.step} ${time}`); }); console.error(`\nTotal Processing Time: ${totalTime}ms`); console.error(`Workflow Success: ${allStepsSuccessful ? 'YES' : 'NO'}`); if (allStepsSuccessful) { console.error('\n🎉 INTEGRATION TEST PASSED'); console.error(' ✅ Multi-tool workflow successfully processes lesson data'); console.error(' ✅ Each step provides precise error isolation capability'); console.error(' ✅ Educational intelligence optimizations applied'); console.error(' ✅ Valid Composer JSON structure generated'); console.error('\n📋 Ready for Production Deployment'); console.error(' The multi-tool architecture is ready to replace the monolithic tool'); console.error(' Expected benefits: Precise 500 error debugging, educational optimizations'); } else { console.error('\n❌ INTEGRATION TEST FAILED'); console.error(' Review failed steps before production deployment'); } console.error(''); } } // Run workflow integration test const tester = new WorkflowIntegrationTester(); tester.runWorkflowTest().catch(console.error);

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