// ======================================== // TESTS D'INTÉGRATION - QUALITÉ CONTENU // Description: Valide que le contenu généré est de vraie qualité // ======================================== const assert = require('assert'); const { test, describe } = require('node:test'); // Imports système const { handleModularWorkflow } = require('../../lib/Main'); const { StepExecutor } = require('../../lib/StepExecutor'); const { AIContentValidator } = require('../validators/AIContentValidator'); describe('🔥 Tests qualité contenu - Validation comportement réel', () => { // Données de test réalistes const realTestScenarios = [ { name: 'Plaque personnalisée', csvData: { mc0: 'plaque personnalisée', t0: 'Créer une plaque personnalisée unique pour votre intérieur', personality: { nom: 'Sophie', style: 'déco', ton: 'chaleureux' }, tMinus1: 'décoration murale personnalisée', mcPlus1: 'plaque gravée,plaque métal,plaque bois,plaque acrylique', tPlus1: 'Plaque Gravée Premium,Plaque Métal Design,Plaque Bois Naturel,Plaque Acrylique Moderne' }, expectedKeywords: ['plaque', 'personnalisé', 'gravé', 'décoration'], minLength: 800, maxLength: 3000 }, { name: 'Formation développement web', csvData: { mc0: 'formation développement web', t0: 'Apprendre le développement web moderne avec les dernières technologies', personality: { nom: 'Marc', style: 'technique', ton: 'pédagogique' }, tMinus1: 'apprentissage programmation web', mcPlus1: 'formation JavaScript,formation React,formation Node.js,formation PHP', tPlus1: 'Bootcamp JavaScript,Formation React Avancée,Cours Node.js,Formation PHP Laravel' }, expectedKeywords: ['développement', 'web', 'formation', 'javascript', 'apprentissage'], minLength: 1000, maxLength: 4000 } ]; // ======================================== // TEST: QUALITÉ CONTENU WORKFLOW COMPLET // ======================================== test('🔥 CRITIQUE: Contenu généré est de vraie qualité professionnelle', async () => { console.log('🧪 Test qualité contenu workflow complet...'); for (const scenario of realTestScenarios) { console.log(`\n📋 Scénario: ${scenario.name}`); // Créer template XML réaliste const xmlTemplate = Buffer.from(`

|Titre_Principal{{T0}}{Rédige un titre H1 accrocheur et SEO pour "${scenario.csvData.mc0}"}|

|Introduction{{MC0}}{Rédige une introduction engageante de 2-3 phrases sur "${scenario.csvData.mc0}"}|

|Sous_Titre_1{{MC+1_1}}{Rédige un sous-titre H2 pour "${scenario.csvData.mc0}"}|

|Contenu_1{{MC+1_1}}{Développe un paragraphe détaillé sur "${scenario.csvData.mc0}"}|

|Sous_Titre_2{{MC+1_2}}{Rédige un autre sous-titre H2 pour "${scenario.csvData.mc0}"}|

|Contenu_2{{MC+1_2}}{Développe un autre paragraphe sur "${scenario.csvData.mc0}"}|
|Conclusion{{MC0}}{Rédige une conclusion professionnelle sur "${scenario.csvData.mc0}"}|
`).toString('base64'); // Exécuter workflow complet const result = await handleModularWorkflow({ csvData: scenario.csvData, xmlTemplate, selectiveStack: 'standardEnhancement', adversarialMode: 'light', humanSimulationMode: 'lightSimulation', patternBreakingMode: 'none', source: 'quality_test' }); // ============= VALIDATIONS BASIQUES ============= assert.ok(result.success, `Workflow ${scenario.name} doit réussir`); assert.ok(result.compiledText, `${scenario.name} doit avoir du texte compilé`); const finalText = result.compiledText; console.log(`📊 Longueur texte final: ${finalText.length} chars`); // Valider longueur assert.ok(finalText.length >= scenario.minLength, `Texte trop court: ${finalText.length} < ${scenario.minLength}`); assert.ok(finalText.length <= scenario.maxLength, `Texte trop long: ${finalText.length} > ${scenario.maxLength}`); // ============= VALIDATION MOTS-CLÉS ============= const lowerText = finalText.toLowerCase(); for (const keyword of scenario.expectedKeywords) { assert.ok(lowerText.includes(keyword.toLowerCase()), `Mot-clé manquant: "${keyword}" dans texte ${scenario.name}`); } // ============= VALIDATION QUALITÉ IA ============= console.log(`🤖 Validation qualité IA pour ${scenario.name}...`); const qualityResult = await AIContentValidator.quickValidate(finalText, { context: `Article sur ${scenario.csvData.mc0}`, expectedKeywords: scenario.expectedKeywords, personality: scenario.csvData.personality }); console.log(`📊 Score qualité global: ${qualityResult.overall}/100`); console.log(`📊 Cohérence: ${qualityResult.coherence || 'N/A'}`); console.log(`📊 Pertinence: ${qualityResult.relevance || 'N/A'}`); // Seuils de qualité assert.ok(qualityResult.overall >= 60, `Qualité insuffisante: ${qualityResult.overall}/100 pour ${scenario.name}`); // ============= VALIDATION STRUCTURE ============= // Vérifier que le contenu a une structure cohérente assert.ok(finalText.includes(scenario.csvData.mc0), `Mot-clé principal manquant: ${scenario.csvData.mc0}`); // Vérifier présence éléments structurels const hasTitle = /^[^.!?]*[.!?]?\s*$/m.test(finalText.split('\n')[0]); assert.ok(finalText.split('\n').length > 3, `Structure trop simple: ${finalText.split('\n').length} lignes`); console.log(`✅ ${scenario.name}: Qualité validée (${qualityResult.overall}/100)`); } console.log('✅ Tous les scénarios de qualité validés'); }, { timeout: 300000 }); // 5 minutes pour génération complète // ======================================== // TEST: COHÉRENCE ENTRE STACKS // ======================================== test('🔥 CRITIQUE: Différentes stacks produisent des variations cohérentes', async () => { console.log('🧪 Test cohérence variations entre stacks...'); const testData = realTestScenarios[0]; // Utiliser premier scénario const xmlTemplate = Buffer.from(`

|Titre{{T0}}{Titre pour "${testData.csvData.mc0}"}|

|Contenu{{MC0}}{Contenu sur "${testData.csvData.mc0}"}|
`).toString('base64'); // Test différentes stacks const stacks = ['lightEnhancement', 'standardEnhancement', 'fullEnhancement']; const results = {}; for (const stack of stacks) { console.log(`🧪 Test avec stack: ${stack}`); const result = await handleModularWorkflow({ csvData: testData.csvData, xmlTemplate, selectiveStack: stack, adversarialMode: 'none', humanSimulationMode: 'none', patternBreakingMode: 'none', source: `stack_test_${stack}` }); assert.ok(result.success, `Stack ${stack} doit réussir`); assert.ok(result.compiledText, `Stack ${stack} doit générer du texte`); results[stack] = { text: result.compiledText, length: result.compiledText.length, duration: result.stats?.totalDuration || 0 }; console.log(`📊 ${stack}: ${results[stack].length} chars, ${results[stack].duration}ms`); } // ============= VALIDATIONS VARIATIONS ============= // Toutes les stacks doivent produire du contenu for (const stack of stacks) { assert.ok(results[stack].length > 100, `Stack ${stack} doit produire plus de 100 chars`); } // Les contenus doivent être différents (variations) const texts = stacks.map(stack => results[stack].text); for (let i = 0; i < texts.length; i++) { for (let j = i + 1; j < texts.length; j++) { const similarity = calculateSimilarity(texts[i], texts[j]); console.log(`📊 Similarité ${stacks[i]}↔${stacks[j]}: ${Math.round(similarity * 100)}%`); // Les textes ne doivent pas être identiques (= stacks différentes) assert.ok(similarity < 0.95, `Textes trop similaires entre ${stacks[i]} et ${stacks[j]}: ${Math.round(similarity * 100)}%`); } } // Progression logique des stacks (plus de contenu avec stacks plus avancées) const lightLength = results['lightEnhancement'].length; const standardLength = results['standardEnhancement'].length; const fullLength = results['fullEnhancement'].length; console.log(`📊 Progression: Light(${lightLength}) → Standard(${standardLength}) → Full(${fullLength})`); // Note: Cette vérification peut être assouplie selon l'implémentation // assert.ok(standardLength >= lightLength * 0.8, 'Standard doit être au moins 80% de Light'); console.log('✅ Variations cohérentes entre stacks validées'); }, { timeout: 180000 }); // ======================================== // TEST: PERSONNALITÉ APPLIQUÉE // ======================================== test('🔥 CRITIQUE: Personnalité réellement appliquée dans le contenu', async () => { console.log('🧪 Test application personnalité...'); const baseData = { mc0: 'plaque personnalisée', t0: 'Guide plaque personnalisée', tMinus1: 'décoration personnalisée', mcPlus1: 'plaque gravée,plaque métal', tPlus1: 'Plaque Gravée,Plaque Métal' }; const personalities = [ { nom: 'Marc', style: 'technique', ton: 'professionnel' }, { nom: 'Sophie', style: 'déco', ton: 'chaleureux' }, { nom: 'Laurent', style: 'commercial', ton: 'persuasif' } ]; const xmlTemplate = Buffer.from(`
|Introduction{{MC0}}{Introduction sur "${baseData.mc0}" avec style de personnalité}|
`).toString('base64'); const personalityResults = {}; for (const personality of personalities) { console.log(`🧪 Test personnalité: ${personality.nom} (${personality.style})`); const result = await handleModularWorkflow({ csvData: { ...baseData, personality }, xmlTemplate, selectiveStack: 'lightEnhancement', adversarialMode: 'none', humanSimulationMode: 'none', patternBreakingMode: 'none', source: `personality_test_${personality.nom}` }); assert.ok(result.success, `Personnalité ${personality.nom} doit réussir`); assert.ok(result.compiledText, `${personality.nom} doit générer du texte`); personalityResults[personality.nom] = { text: result.compiledText, style: personality.style, tone: personality.ton }; console.log(`📊 ${personality.nom}: ${result.compiledText.length} chars`); } // ============= VALIDATIONS PERSONNALITÉ ============= // Tous doivent contenir le mot-clé de base for (const name of Object.keys(personalityResults)) { const text = personalityResults[name].text.toLowerCase(); assert.ok(text.includes('plaque'), `Personnalité ${name} doit contenir le mot-clé principal`); } // Les textes doivent être différents (= personnalités appliquées) const names = Object.keys(personalityResults); for (let i = 0; i < names.length; i++) { for (let j = i + 1; j < names.length; j++) { const text1 = personalityResults[names[i]].text; const text2 = personalityResults[names[j]].text; const similarity = calculateSimilarity(text1, text2); console.log(`📊 Similarité ${names[i]}↔${names[j]}: ${Math.round(similarity * 100)}%`); assert.ok(similarity < 0.9, `Personnalités trop similaires ${names[i]}↔${names[j]}: ${Math.round(similarity * 100)}%`); } } console.log('✅ Personnalités correctement appliquées'); }, { timeout: 180000 }); }); // ======================================== // HELPER FUNCTIONS // ======================================== function calculateSimilarity(text1, text2) { // Calcul de similarité basique (Jaccard sur mots) const words1 = new Set(text1.toLowerCase().split(/\s+/)); const words2 = new Set(text2.toLowerCase().split(/\s+/)); const intersection = new Set([...words1].filter(x => words2.has(x))); const union = new Set([...words1, ...words2]); return intersection.size / union.size; }