// ======================================== // GÉNÉRATEUR AUTOMATIQUE DE TESTS // Scan des modules lib/ et génération de tests systématiques // ======================================== const fs = require('fs').promises; const path = require('path'); const { logSh } = require('../../lib/ErrorReporting'); /** * Générateur automatique de tests pour tous les modules lib/ * Analyse AST et génère des tests adaptés au type de fonction */ class ModuleTestGenerator { static LIB_PATH = path.join(__dirname, '../../lib'); static GENERATED_TESTS_PATH = path.join(__dirname, './generated'); // Types de fonctions identifiables static FUNCTION_TYPES = { ASYNC: 'async', SYNC: 'sync', CONTENT_GENERATOR: 'content', VALIDATOR: 'validator', UTILITY: 'utility', CLASS_METHOD: 'method', EXPORT_FUNCTION: 'export' }; /** * 🚀 GÉNÉRATION COMPLÈTE TESTS - ORCHESTRATEUR PRINCIPAL * * CE QUI EST TESTÉ : * ✅ Scan récursif dossier lib/ pour détecter tous modules .js * ✅ Génération automatique tests adaptés par type fonction * ✅ Création master test runner pour exécution batch * ✅ Statistiques complètes : générés, échecs, couverture * * ALGORITHMES EXÉCUTÉS : * - scanLibModules() : scan récursif avec filter .js non-test * - Pour chaque module : analyzeModule() + generateTestCases() + writeTestFile() * - generateMasterTestRunner() pour orchestration globale * - Logging détaillé avec ErrorReporting.logSh() */ static async generateAllTests() { try { logSh('🔧 Démarrage génération automatique des tests', 'INFO'); // Création du dossier de tests générés await this.ensureGeneratedDirectory(); // Scan de tous les modules lib/ const modules = await this.scanLibModules(); logSh(`📦 ${modules.length} modules détectés`, 'DEBUG'); const results = { generated: 0, skipped: 0, errors: 0, modules: [] }; // Génération pour chaque module for (const modulePath of modules) { try { const moduleResult = await this.generateTestsForModule(modulePath); results.modules.push(moduleResult); results.generated++; logSh(`✅ Tests générés: ${path.basename(modulePath)}`, 'DEBUG'); } catch (error) { logSh(`❌ Erreur génération ${path.basename(modulePath)}: ${error.message}`, 'ERROR'); results.errors++; } } // Génération du test runner global await this.generateMasterTestRunner(results.modules); logSh(`🎯 Génération terminée: ${results.generated} générés, ${results.errors} erreurs`, 'INFO'); return results; } catch (error) { logSh(`💥 Erreur génération globale: ${error.message}`, 'ERROR'); throw error; } } /** * 🎯 GÉNÉRATION TESTS MODULE SPÉCIFIQUE - PIPELINE INDIVIDUEL * * CE QUI EST TESTÉ : * ✅ Analyse statique complète : fonctions, classes, exports, dépendances * ✅ Génération cas tests adaptés au type (async, validator, generator, etc.) * ✅ Écriture fichier test avec imports et structure Node.js * ✅ Calcul métrique couverture (% fonctions testées) * * ALGORITHMES EXÉCUTÉS : * - analyzeModule() pour extraction AST complète * - generateTestCases() avec templates spécialisés * - writeTestFile() avec structure Node.js Test Runner * - calculateCoverage() pour métriques qualité */ static async generateTestsForModule(modulePath) { const analysis = await this.analyzeModule(modulePath); const tests = await this.generateTestCases(analysis); const testFilePath = await this.writeTestFile(analysis, tests); return { modulePath, testFilePath, analysis, testCount: tests.length, coverage: this.calculateCoverage(analysis, tests) }; } /** * 🔍 ANALYSE MODULE - EXTRACTEUR AST COMPLET * * CE QUI EST TESTÉ : * ✅ Lecture fichier + parsing relatif paths pour imports * ✅ Extraction 4 types : fonctions, classes, exports, dépendances * ✅ Analyse signature fonctions (async, params, type) * ✅ Construction structure analysis unifiée * * ALGORITHMES EXÉCUTÉS : * - fs.readFile() + path.relative() pour chemins * - extractFunctions() avec regex patterns multiples * - extractClasses() + extractExports() + extractDependencies() * - Return analysis object complet pour génération tests */ static async analyzeModule(modulePath) { const content = await fs.readFile(modulePath, 'utf8'); const relativePath = path.relative(this.LIB_PATH, modulePath); const moduleName = path.basename(modulePath, '.js'); const analysis = { modulePath, relativePath, moduleName, content, functions: [], classes: [], exports: [], dependencies: [] }; // Extraction des fonctions analysis.functions = this.extractFunctions(content); // Extraction des classes analysis.classes = this.extractClasses(content); // Extraction des exports analysis.exports = this.extractExports(content); // Extraction des dépendances analysis.dependencies = this.extractDependencies(content); return analysis; } /** * 🔧 EXTRACTION FONCTIONS - PARSER REGEX AVANCÉ * * CE QUI EST TESTÉ : * ✅ 4 patterns regex : async function, function normale, arrow functions, méthodes classe * ✅ Extraction nom + paramètres + détection async * ✅ Classification automatique type fonction (generator, validator, utility, etc.) * ✅ Déduplication et filtrage (skip constructor, méthodes privées) * * ALGORITHMES EXÉCUTÉS : * - 4 regex patterns avec exec() loop pour toutes occurrences * - determineFunctionType() selon nom et signature * - Params parsing : split(',') + trim + filter vides * - Dedup avec find() pour éviter doublons par nom */ static extractFunctions(content) { const functions = []; // Patterns pour différents types de fonctions const patterns = [ // Fonctions async /async\s+function\s+(\w+)\s*\(([^)]*)\)/g, // Fonctions normales /(?:^|\n)\s*function\s+(\w+)\s*\(([^)]*)\)/g, // Fonctions fléchées avec nom /(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\([^)]*\)\s*=>/g, // Méthodes de classe /(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/g ]; patterns.forEach((pattern, index) => { let match; while ((match = pattern.exec(content)) !== null) { const functionName = match[1]; const params = match[2] || ''; // Skip constructors et méthodes privées if (functionName === 'constructor' || functionName.startsWith('_')) continue; const func = { name: functionName, params: params.split(',').map(p => p.trim()).filter(p => p), isAsync: match[0].includes('async'), type: this.determineFunctionType(functionName, match[0], content), fullMatch: match[0], index: match.index }; // Éviter les doublons if (!functions.find(f => f.name === functionName)) { functions.push(func); } } }); return functions; } /** * 🏢 EXTRACTION CLASSES - DÉTECTEUR ORIENTÉ OBJET * * CE QUI EST TESTÉ : * ✅ Regex pattern class avec support héritage (extends) * ✅ Extraction méthodes classe via extractClassMethods() * ✅ Construction structure class complète avec metadata * * ALGORITHMES EXÉCUTÉS : * - Pattern : /class\s+(\w+)(?:\s+extends\s+(\w+))?\s*\{/g * - Pour chaque match : extraction nom + parent class * - extractClassMethods() pour lister méthodes publiques * - Return array classes avec nom, extends, méthodes */ static extractClasses(content) { const classes = []; const classPattern = /class\s+(\w+)(?:\s+extends\s+(\w+))?\s*\{/g; let match; while ((match = classPattern.exec(content)) !== null) { classes.push({ name: match[1], extends: match[2], methods: this.extractClassMethods(content, match.index) }); } return classes; } /** * 📦 EXTRACTION EXPORTS - DÉTECTEUR MODULE INTERFACE * * CE QUI EST TESTÉ : * ✅ 2 patterns : module.exports = {...} et exports.functionName * ✅ Parsing object exports avec support syntax variations * ✅ Déduplication avec Set pour éviter doublons * * ALGORITHMES EXÉCUTÉS : * - Pattern 1 : /module\.exports\s*=\s*\{([^}]+)\}/s pour block exports * - Pattern 2 : /exports\.(\w+)/g pour named exports individuels * - Parsing object syntax avec split(':') pour key:value * - [...new Set(exports)] pour déduplication finale */ static extractExports(content) { const exports = []; // module.exports = { ... } const moduleExportsMatch = content.match(/module\.exports\s*=\s*\{([^}]+)\}/s); if (moduleExportsMatch) { const exportsContent = moduleExportsMatch[1]; const exportNames = exportsContent.match(/(\w+)(?:\s*:\s*\w+)?/g) || []; exports.push(...exportNames.map(name => name.split(':')[0].trim())); } // exports.functionName const namedExportsPattern = /exports\.(\w+)/g; let match; while ((match = namedExportsPattern.exec(content)) !== null) { exports.push(match[1]); } return [...new Set(exports)]; // Déduplication } /** * 🔗 EXTRACTION DÉPENDANCES - DÉTECTEUR IMPORTS * * CE QUI EST TESTÉ : * ✅ Détection statements require() avec support quotes multiples * ✅ Extraction noms modules pour génération imports tests * ✅ Support syntax variations : require('module'), require("module") * * ALGORITHME EXÉCUTÉ : * - Pattern : /require\(['\"\`]([^'\"\`]+)['\"\`]\)/g * - Exec loop pour toutes occurrences * - Capture groupe 1 pour nom module * - Return array pour imports générés */ static extractDependencies(content) { const dependencies = []; // require statements const requirePattern = /require\(['"`]([^'"`]+)['"`]\)/g; let match; while ((match = requirePattern.exec(content)) !== null) { dependencies.push(match[1]); } return dependencies; } /** * 🏷️ DÉTERMINATION TYPE FONCTION - CLASSIFICATEUR INTELLIGENT * * CE QUI EST TESTÉ : * ✅ Classification 6 types selon nom et signature * ✅ CONTENT_GENERATOR : generate|create|build dans nom * ✅ VALIDATOR : validate|check|verify dans nom * ✅ ASYNC : signature.includes('async') * ✅ UTILITY : format|parse|calculate dans nom * ✅ Fallback SYNC pour autres * * ALGORITHME EXÉCUTÉ : * - Séquence if conditions avec includes() sur nom fonction * - Priority order : generator > validator > async > utility > sync * - Return FUNCTION_TYPES enum pour template selection */ static determineFunctionType(name, signature, content) { // Fonction de génération de contenu if (name.includes('generate') || name.includes('create') || name.includes('build')) { return this.FUNCTION_TYPES.CONTENT_GENERATOR; } // Fonction de validation if (name.includes('validate') || name.includes('check') || name.includes('verify')) { return this.FUNCTION_TYPES.VALIDATOR; } // Fonction async if (signature.includes('async')) { return this.FUNCTION_TYPES.ASYNC; } // Fonction utilitaire if (name.includes('format') || name.includes('parse') || name.includes('calculate')) { return this.FUNCTION_TYPES.UTILITY; } return this.FUNCTION_TYPES.SYNC; } /** * 🧪 GÉNÉRATION CAS TESTS - FACTORY TEMPLATE TESTS * * CE QUI EST TESTÉ : * ✅ Génération tests fonctions avec templates spécialisés * ✅ Génération tests exports (vérification disponibilité) * ✅ Test intégration général module health check * ✅ Selection template selon function type * * ALGORITHMES EXÉCUTÉS : * - Loop analysis.functions : generateFunctionTest() pour chaque * - Loop analysis.exports : generateExportTest() pour disponibilité * - generateIntegrationTest() pour health check global * - Return array complet tests pour writeTestFile() */ static async generateTestCases(analysis) { const tests = []; // Tests pour les fonctions for (const func of analysis.functions) { const testCase = await this.generateFunctionTest(func, analysis); if (testCase) tests.push(testCase); } // Tests pour les exports for (const exportName of analysis.exports) { const exportTest = this.generateExportTest(exportName, analysis); if (exportTest) tests.push(exportTest); } // Test d'intégration général tests.push(this.generateIntegrationTest(analysis)); return tests; } /** * 🎯 GÉNÉRATION TEST FONCTION - TEMPLATE SELECTOR * * CE QUI EST TESTÉ : * ✅ Switch selon function type pour template approprié * ✅ Templates spécialisés : ContentGenerator, Validator, Async, Basic * ✅ Génération inputs mockés selon paramètres fonction * ✅ Structure test object avec metadata * * ALGORITHMES EXÉCUTÉS : * - Switch func.type pour sélection template method * - getContentGeneratorTestTemplate() avec validation IA * - getValidatorTestTemplate() avec valid/invalid inputs * - getAsyncTestTemplate() avec timeout et duration * - getBasicTestTemplate() pour fonctions standard */ static async generateFunctionTest(func, analysis) { let testTemplate; switch (func.type) { case this.FUNCTION_TYPES.CONTENT_GENERATOR: testTemplate = this.getContentGeneratorTestTemplate(func, analysis); break; case this.FUNCTION_TYPES.VALIDATOR: testTemplate = this.getValidatorTestTemplate(func, analysis); break; case this.FUNCTION_TYPES.ASYNC: testTemplate = this.getAsyncTestTemplate(func, analysis); break; default: testTemplate = this.getBasicTestTemplate(func, analysis); } return { name: `${func.name}_test`, type: func.type, template: testTemplate, function: func }; } /** * 📝 TEMPLATE CONTENT GENERATOR - TEST GÉNÉRATION CONTENU * * CE QUI EST TESTÉ : * ✅ Exécution fonction génération avec mock input * ✅ Validations base : result exists, type string/object * ✅ Validation qualité IA si contenu texte >50 chars * ✅ Seuil qualité : AIContentValidator score >= 40 * * ALGORITHME EXÉCUTÉ : * - Génération mockInput adapté aux paramètres * - Assert basiques : result truthy + type check * - If string + length>50 : AIContentValidator.quickValidate() * - Assert validation.overall >= 40 pour qualité acceptable */ static getContentGeneratorTestTemplate(func, analysis) { return ` test('${func.name} - Content Generation', async () => { const mockInput = ${this.generateMockInput(func)}; try { const result = await ${analysis.moduleName}.${func.name}(mockInput); // Validations de base assert.ok(result, 'Should return a result'); assert.ok(typeof result === 'string' || typeof result === 'object', 'Should return content'); // Validation IA si contenu texte if (typeof result === 'string' && result.length > 50) { const validation = await AIContentValidator.quickValidate(result, { context: 'Generated content test' }); assert.ok(validation.overall >= 40, 'Content quality should be acceptable'); } console.log('✅ ${func.name}: Content generated successfully'); } catch (error) { console.error('❌ ${func.name}: Generation failed:', error.message); throw error; } });`; } /** * ✅ TEMPLATE VALIDATOR - TEST FONCTION VALIDATION * * CE QUI EST TESTÉ : * ✅ Test input valide : doit retourner résultat (pas undefined) * ✅ Test input invalide : doit retourner false ou throw error * ✅ Gestion gracieuse erreurs attendues validation * ✅ Vérification comportement correct selon type retour * * ALGORITHMES EXÉCUTÉS : * - generateValidInput() + generateInvalidInput() pour cas tests * - Assert validResult !== undefined pour success case * - Try/catch sur invalid : si boolean=false OU exception OK * - Console.log expected errors pour debugging */ static getValidatorTestTemplate(func, analysis) { return ` test('${func.name} - Validation', async () => { const validInput = ${this.generateValidInput(func)}; const invalidInput = ${this.generateInvalidInput(func)}; try { // Test avec input valide const validResult = await ${analysis.moduleName}.${func.name}(validInput); assert.ok(validResult !== undefined, 'Should return result for valid input'); // Test avec input invalide try { const invalidResult = await ${analysis.moduleName}.${func.name}(invalidInput); // Si pas d'erreur, vérifier que la validation échoue if (typeof invalidResult === 'boolean') { assert.strictEqual(invalidResult, false, 'Should return false for invalid input'); } } catch (error) { // C'est attendu pour une validation qui throw console.log('Expected validation error:', error.message); } console.log('✅ ${func.name}: Validation working correctly'); } catch (error) { console.error('❌ ${func.name}: Validation test failed:', error.message); throw error; } });`; } /** * ⏱️ TEMPLATE ASYNC - TEST FONCTION ASYNCHRONE * * CE QUI EST TESTÉ : * ✅ Exécution async avec mesure durée (performance) * ✅ Timeout 30 secondes max (assert duration < 30000ms) * ✅ Vérification résultat non undefined * ✅ Logging durée exécution pour monitoring * * ALGORITHMES EXÉCUTÉS : * - Date.now() avant/après pour mesure durée * - await func() avec mockInput généré * - Assert result + assert duration < 30000 * - Console.log avec template literal pour durée */ static getAsyncTestTemplate(func, analysis) { return ` test('${func.name} - Async Operation', async () => { const input = ${this.generateMockInput(func)}; try { const startTime = Date.now(); const result = await ${analysis.moduleName}.${func.name}(input); const duration = Date.now() - startTime; // Validations de base assert.ok(result !== undefined, 'Should return a result'); assert.ok(duration < 30000, 'Should complete within 30 seconds'); console.log(\`✅ ${func.name}: Completed in \${duration}ms\`); } catch (error) { console.error('❌ ${func.name}: Async operation failed:', error.message); throw error; } });`; } /** * 🔧 TEMPLATE BASIC - TEST FONCTION STANDARD * * CE QUI EST TESTÉ : * ✅ Exécution synchrone fonction avec mock input * ✅ Validations minimales : result !== undefined * ✅ Type checking basique pour éviter crashes * ✅ Gestion erreurs avec logging pour debugging * * ALGORITHME EXÉCUTÉ : * - Génération mockInput via generateMockInput() * - Appel direct fonction (pas await) * - Double assert : !== undefined + typeof check * - Try/catch avec console.error pour troubleshooting */ static getBasicTestTemplate(func, analysis) { return ` test('${func.name} - Basic Function', () => { const input = ${this.generateMockInput(func)}; try { const result = ${analysis.moduleName}.${func.name}(input); // Validations de base assert.ok(result !== undefined, 'Should return a result'); assert.ok(typeof result !== 'undefined', 'Result should be defined'); console.log('✅ ${func.name}: Function executed successfully'); } catch (error) { console.error('❌ ${func.name}: Function failed:', error.message); throw error; } });`; } /** * 🎭 GÉNÉRATION INPUTS MOCKÉS - FACTORY DONNÉES TEST * * CE QUI EST TESTÉ : * ✅ Génération données test adaptées selon noms paramètres * ✅ Mapping intelligent : 'content' → texte, 'data' → CSV object, etc. * ✅ Support paramètres multiples avec array notation * ✅ Fallback 'test_value' pour paramètres non reconnus * * ALGORITHMES EXÉCUTÉS : * - If params.length=0 : return 'undefined' * - Map params : content→text, data→CSV, personality→profile, options→config * - Single param : return direct, multiple : return [array] * - Smart defaults pour testing réaliste */ static generateMockInput(func) { if (func.params.length === 0) return 'undefined'; const mockInputs = func.params.map(param => { if (param.includes('content')) return '"Test content for validation"'; if (param.includes('data') || param.includes('csv')) return '{ mc0: "test keyword", t0: "Test title" }'; if (param.includes('personality')) return '{ nom: "Marc", style: "technique" }'; if (param.includes('options')) return '{ test: true }'; if (param.includes('text')) return '"Sample text for processing"'; return '"test_value"'; }); return mockInputs.length === 1 ? mockInputs[0] : `[${mockInputs.join(', ')}]`; } static generateValidInput(func) { return this.generateMockInput(func); } static generateInvalidInput(func) { if (func.params.length === 0) return 'undefined'; return 'null'; } /** * 📝 ÉCRITURE FICHIER TEST - GÉNÉRATEUR FICHIER * * CE QUI EST TESTÉ : * ✅ Construction nom fichier : moduleName + '.generated.test.js' * ✅ Génération contenu complet avec buildTestFileContent() * ✅ Écriture fichier dans dossier GENERATED_TESTS_PATH * ✅ Return path pour référence dans results * * ALGORITHME EXÉCUTÉ : * - path.join(GENERATED_TESTS_PATH, fileName) pour chemin complet * - buildTestFileContent() avec imports + tests + structure * - fs.writeFile() avec encoding utf8 * - Return testFilePath pour tracking */ static async writeTestFile(analysis, tests) { const testFileName = `${analysis.moduleName}.generated.test.js`; const testFilePath = path.join(this.GENERATED_TESTS_PATH, testFileName); const testContent = this.buildTestFileContent(analysis, tests); await fs.writeFile(testFilePath, testContent, 'utf8'); return testFilePath; } /** * 🏧 CONSTRUCTION CONTENU FICHIER - BUILDER TEMPLATE COMPLET * * CE QUI EST TESTÉ : * ✅ Header avec metadata : module path, date génération, etc. * ✅ Imports automatiques : assert, node:test, module cible + dépendances * ✅ Structure describe() avec setup + tests générés + integration * ✅ Test module loading + health check avec exports listing * * ALGORITHMES EXÉCUTÉS : * - generateImports() pour imports nécessaires (IA validator si content tests) * - Template string avec placeholders : moduleName, date, tests * - Concat tests.map(test => test.template) pour tous cas * - Integration test avec Object.keys() pour exports check */ static buildTestFileContent(analysis, tests) { const imports = this.generateImports(analysis); const testCases = tests.map(test => test.template).join('\n'); return `// ======================================== // TESTS GÉNÉRÉS AUTOMATIQUEMENT - ${analysis.moduleName} // Module: ${analysis.relativePath} // Générés le: ${new Date().toISOString()} // ======================================== const assert = require('assert'); const { test, describe } = require('node:test'); ${imports} describe('${analysis.moduleName} - Tests automatiques', () => { // Setup avant les tests let testContext = {}; test('Module loading', () => { assert.ok(${analysis.moduleName}, 'Module should be loaded'); console.log('📦 Module ${analysis.moduleName} loaded successfully'); }); ${testCases} // Test d'intégration général test('Integration - Module health check', async () => { try { // Vérification exports const exports = Object.keys(${analysis.moduleName}); assert.ok(exports.length > 0, 'Module should export functions'); console.log(\`✅ ${analysis.moduleName}: \${exports.length} exports available\`); console.log('📋 Exports:', exports.join(', ')); } catch (error) { console.error('❌ Integration test failed:', error.message); throw error; } }); });`; } /** * 📦 GÉNÉRATION IMPORTS NÉCESSAIRES - DÉTECTEUR DÉPENDANCES * * CE QUI EST TESTÉ : * ✅ Import module cible avec chemin relatif corrigé * ✅ Détection tests contenu pour import AIContentValidator * ✅ Construction array imports pour injection template * ✅ Chemins relatifs adaptés structure dossiers * * ALGORITHME EXÉCUTÉ : * - Import base : require('../../relativePath') pour module * - hasContentTests = analysis.functions avec CONTENT_GENERATOR type * - If hasContentTests : add AIContentValidator import * - Return imports.join('\n') pour template injection */ static generateImports(analysis) { const imports = [`const ${analysis.moduleName} = require('../../${analysis.relativePath}');`]; // Import du validateur IA si tests de contenu const hasContentTests = analysis.functions.some(f => f.type === this.FUNCTION_TYPES.CONTENT_GENERATOR ); if (hasContentTests) { imports.push("const { AIContentValidator } = require('../validators/AIContentValidator');"); } return imports.join('\n'); } /** * 📊 CALCUL COUVERTURE - MÉTRIQUE QUALITÉ TESTS * * CE QUI EST TESTÉ : * ✅ Comptage total fonctions + exports disponibles * ✅ Comptage tests générés (exclusion type integration) * ✅ Calcul pourcentage couverture avec protection division zéro * ✅ Return métriques détaillées pour reporting * * ALGORITHMES EXÉCUTÉS : * - totalFunctions = analysis.functions.length + analysis.exports.length * - testedFunctions = tests.filter(t => t.type !== 'integration').length * - percentage = Math.round((tested/total)*100) avec protection >0 * - Return {total, tested, percentage} pour dashboard */ static calculateCoverage(analysis, tests) { const totalFunctions = analysis.functions.length + analysis.exports.length; const testedFunctions = tests.filter(t => t.type !== 'integration').length; return { total: totalFunctions, tested: testedFunctions, percentage: totalFunctions > 0 ? Math.round((testedFunctions / totalFunctions) * 100) : 0 }; } /** * 📁 UTILITAIRE CRÉATION DOSSIER - SETUP ENVIRONNEMENT * * CE QUI EST TESTÉ : * ✅ Vérification existence dossier GENERATED_TESTS_PATH * ✅ Création automatique si inexistant avec recursive:true * ✅ Gestion gracieuse erreurs accès fichiers * * ALGORITHME EXÉCUTÉ : * - fs.access() pour test existence * - Si erreur : fs.mkdir() avec {recursive:true} * - Try/catch pattern pour gestion erreurs */ static async ensureGeneratedDirectory() { try { await fs.access(this.GENERATED_TESTS_PATH); } catch (error) { await fs.mkdir(this.GENERATED_TESTS_PATH, { recursive: true }); } } /** * 🔍 SCAN MODULES LIB - EXPLORATEUR RÉCURSIF * * CE QUI EST TESTÉ : * ✅ Scan récursif dossier lib/ avec exploration sous-dossiers * ✅ Filtrage .js files excluant fichiers test existants * ✅ Construction liste complète chemins absolus modules * ✅ Support structure dossiers imbriqués (adversarial/, generation/, etc.) * * ALGORITHMES EXÉCUTÉS : * - scanDirectory() récursif avec fs.readdir({withFileTypes:true}) * - Pour chaque item : if directory → recurse, if .js file → add * - Filter : item.name.endsWith('.js') && !item.name.includes('test') * - Return array paths absolus pour generateAllTests() */ static async scanLibModules() { const modules = []; const scanDirectory = async (dirPath) => { const items = await fs.readdir(dirPath, { withFileTypes: true }); for (const item of items) { const fullPath = path.join(dirPath, item.name); if (item.isDirectory()) { await scanDirectory(fullPath); } else if (item.isFile() && item.name.endsWith('.js') && !item.name.includes('test')) { modules.push(fullPath); } } }; await scanDirectory(this.LIB_PATH); return modules; } /** * 🎯 EXTRACTION MÉTHODES CLASSE - PARSER ORIENTÉ OBJET * * CE QUI EST TESTÉ : * ✅ Extraction méthodes depuis position début classe * ✅ Support async methods avec pattern flexible * ✅ Exclusion constructor (pas testé automatiquement) * ✅ Return array noms méthodes pour tests génération * * ALGORITHME EXÉCUTÉ : * - substring(classStartIndex) pour focus contenu classe * - Pattern : /(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/g * - Filter !== 'constructor' pour méthodes publiques * - Return methods array pour class analysis */ static extractClassMethods(content, classStartIndex) { // Extraction simplifiée des méthodes de classe const methods = []; const classContent = content.substring(classStartIndex); const methodPattern = /(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/g; let match; while ((match = methodPattern.exec(classContent)) !== null) { if (match[1] !== 'constructor') { methods.push(match[1]); } } return methods; } /** * 📤 GÉNÉRATION TEST EXPORT - VÉRIFICATEUR INTERFACE * * CE QUI EST TESTÉ : * ✅ Vérification disponibilité export (!== undefined) * ✅ Test simple existence pour interface module * ✅ Logging success pour debugging * ✅ Structure test object standardisée * * ALGORITHME EXÉCUTÉ : * - Assert moduleName.exportName !== undefined * - Template minimal avec console.log success * - Return test object avec name, type:'export', template, exportName */ static generateExportTest(exportName, analysis) { return { name: `export_${exportName}_test`, type: 'export', template: ` test('Export - ${exportName}', () => { assert.ok(${analysis.moduleName}.${exportName} !== undefined, 'Export ${exportName} should be available'); console.log('✅ Export ${exportName}: Available'); });`, exportName }; } static generateIntegrationTest(analysis) { return { name: 'integration_test', type: 'integration', template: '', // Géré dans buildTestFileContent analysis }; } /** * 🎨 GÉNÉRATION MASTER TEST RUNNER - ORCHESTRATEUR GLOBAL * * CE QUI EST TESTÉ : * ✅ Création runner exécutant tous tests générés en séquence * ✅ Gestion spawn Node.js avec --test flag pour chaque fichier * ✅ Comptage passed/failed avec exit code approprié * ✅ Logging détaillé pour monitoring execution * * ALGORITHMES EXÉCUTÉS : * - Génération TEST_FILES array depuis moduleResults paths * - runSingleTest() avec spawn('node', ['--test', testPath]) * - Promise wrapper pour child process avec close/error events * - Stats tracking + process.exit(1) si failures */ static async generateMasterTestRunner(moduleResults) { const runnerPath = path.join(this.GENERATED_TESTS_PATH, 'test-runner-generated.js'); const runnerContent = `// ======================================== // RUNNER TESTS GÉNÉRÉS AUTOMATIQUEMENT // Exécute tous les tests générés pour les modules lib/ // ======================================== const { spawn } = require('child_process'); const path = require('path'); const TEST_FILES = [ ${moduleResults.map(r => ` '${path.basename(r.testFilePath)}'`).join(',\n')} ]; async function runGeneratedTests() { console.log('🧪 === TESTS GÉNÉRÉS AUTOMATIQUEMENT ==='); console.log(\`📦 \${TEST_FILES.length} fichiers de test détectés\`); let passed = 0; let failed = 0; for (const testFile of TEST_FILES) { console.log(\`\n🔍 Test: \${testFile}\`); try { await runSingleTest(testFile); passed++; console.log(\`✅ \${testFile}: PASSED\`); } catch (error) { failed++; console.log(\`❌ \${testFile}: FAILED - \${error.message}\`); } } console.log(\`\n📊 RÉSULTATS: \${passed} passed, \${failed} failed\`); if (failed > 0) { process.exit(1); } } function runSingleTest(testFile) { return new Promise((resolve, reject) => { const testPath = path.join(__dirname, testFile); const child = spawn('node', ['--test', testPath], { stdio: 'inherit' }); child.on('close', (code) => { if (code === 0) { resolve(); } else { reject(new Error(\`Test failed with code \${code}\`)); } }); child.on('error', reject); }); } // Exécution si appelé directement if (require.main === module) { runGeneratedTests().catch(error => { console.error('💥 Erreur runner:', error.message); process.exit(1); }); } module.exports = { runGeneratedTests };`; await fs.writeFile(runnerPath, runnerContent, 'utf8'); logSh(`📝 Test runner généré: ${runnerPath}`, 'DEBUG'); } } module.exports = { ModuleTestGenerator };