UNPKG

aios-core

Version:

Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework

1,175 lines (944 loc) 37.1 kB
--- ## Execution Modes **Choose your execution mode:** ### 1. YOLO Mode - Fast, Autonomous (0-1 prompts) - Autonomous decision making with logging - Minimal user interaction - **Best for:** Simple, deterministic tasks ### 2. Interactive Mode - Balanced, Educational (5-10 prompts) **[DEFAULT]** - Explicit decision checkpoints - Educational explanations - **Best for:** Learning, complex decisions ### 3. Pre-Flight Planning - Comprehensive Upfront Planning - Task analysis phase (identify all ambiguities) - Zero ambiguity execution - **Best for:** Ambiguous requirements, critical work **Parameter:** `mode` (optional, default: `interactive`) --- ## Task Definition (AIOS Task Format V1.0) ```yaml task: qaGenerateTests() responsável: Quinn (Guardian) responsavel_type: Agente atomic_layer: Config **Entrada:** - campo: target tipo: string origem: User Input obrigatório: true validação: Must exist - campo: criteria tipo: array origem: config obrigatório: true validação: Non-empty validation criteria - campo: strict tipo: boolean origem: User Input obrigatório: false validação: Default: true **Saída:** - campo: validation_result tipo: boolean destino: Return value persistido: false - campo: errors tipo: array destino: Memory persistido: false - campo: report tipo: object destino: File (.ai/*.json) persistido: true ``` --- ## Pre-Conditions **Purpose:** Validate prerequisites BEFORE task execution (blocking) **Checklist:** ```yaml pre-conditions: - [ ] Validation rules loaded; target available for validation tipo: pre-condition blocker: true validação: | Check validation rules loaded; target available for validation error_message: "Pre-condition failed: Validation rules loaded; target available for validation" ``` --- ## Post-Conditions **Purpose:** Validate execution success AFTER task completes **Checklist:** ```yaml post-conditions: - [ ] Validation executed; results accurate; report generated tipo: post-condition blocker: true validação: | Verify validation executed; results accurate; report generated error_message: "Post-condition failed: Validation executed; results accurate; report generated" ``` --- ## Acceptance Criteria **Purpose:** Definitive pass/fail criteria for task completion **Checklist:** ```yaml acceptance-criteria: - [ ] Validation rules applied; pass/fail accurate; actionable feedback tipo: acceptance-criterion blocker: true validação: | Assert validation rules applied; pass/fail accurate; actionable feedback error_message: "Acceptance criterion not met: Validation rules applied; pass/fail accurate; actionable feedback" ``` --- ## Tools **External/shared resources used by this task:** - **Tool:** validation-engine - **Purpose:** Rule-based validation and reporting - **Source:** .aios-core/utils/validation-engine.js - **Tool:** schema-validator - **Purpose:** JSON/YAML schema validation - **Source:** ajv or similar --- ## Scripts **Agent-specific code for this task:** - **Script:** run-validation.js - **Purpose:** Execute validation rules and generate report - **Language:** JavaScript - **Location:** .aios-core/scripts/run-validation.js --- ## Error Handling **Strategy:** retry **Common Errors:** 1. **Error:** Validation Criteria Missing - **Cause:** Required validation rules not defined - **Resolution:** Ensure validation criteria loaded from config - **Recovery:** Use default validation rules, log warning 2. **Error:** Invalid Schema - **Cause:** Target does not match expected schema - **Resolution:** Update schema or fix target structure - **Recovery:** Detailed validation error report 3. **Error:** Dependency Missing - **Cause:** Required dependency for validation not found - **Resolution:** Install missing dependencies - **Recovery:** Abort with clear dependency list --- ## Performance **Expected Metrics:** ```yaml duration_expected: 2-10 min (estimated) cost_estimated: $0.001-0.008 token_usage: ~800-2,500 tokens ``` **Optimization Notes:** - Validate configuration early; use atomic writes; implement rollback checkpoints --- ## Metadata ```yaml story: N/A version: 1.0.0 dependencies: - N/A tags: - quality-assurance - testing updated_at: 2025-11-17 ``` --- tools: - github-cli # TODO: Create test-generation-checklist.md for validation (follow-up story needed) # checklists: # - test-generation-checklist.md --- # Generate Tests - AIOS Developer Task ## Purpose Automatically generate comprehensive test suites for framework components using AI analysis and template systems. ## Command Pattern ``` *generate-tests <component-type> <component-name> [options] ``` ## Parameters - `component-type`: Type of component (agent, task, workflow, util) - `component-name`: Name/ID of the component to generate tests for - `options`: Test generation configuration ### Options - `--test-type <type>`: Type of tests to generate (unit, integration, e2e, all) - `--coverage-target <percentage>`: Target code coverage percentage (default: 80) - `--framework <name>`: Test framework to use (jest, mocha, vitest) - `--mock-level <level>`: Mocking level (minimal, moderate, extensive) - `--update-existing`: Update existing test files instead of creating new ones - `--quality-level <level>`: Test quality level (basic, standard, comprehensive) - `--output-dir <path>`: Custom output directory for generated tests ## Examples ```bash # Generate unit tests for an agent *generate-tests agent weather-fetcher --test-type unit --coverage-target 90 # Generate comprehensive test suite for a utility *generate-tests util logger --test-type all --quality-level comprehensive # Update existing tests for a task *generate-tests task data-processor --update-existing --coverage-target 85 # Generate integration tests with extensive mocking *generate-tests workflow user-onboarding --test-type integration --mock-level extensive # Generate tests for all component types *generate-tests all components --framework vitest --output-dir tests/auto-generated ``` ## Implementation ```javascript const fs = require('fs').promises; const path = require('path'); const chalk = require('chalk'); const inquirer = require('inquirer'); class GenerateTestsTask { constructor() { this.taskName = 'generate-tests'; this.description = 'Generate comprehensive test suites for framework components'; this.rootPath = process.cwd(); this.testTemplateSystem = null; this.testGenerator = null; this.coverageAnalyzer = null; this.qualityAssessment = null; } async execute(params) { try { console.log(chalk.blue('🧪 AIOS Test Generation')); console.log(chalk.gray('Generating comprehensive test suites for components\n')); // Parse and validate parameters const config = await this.parseParameters(params); // Initialize dependencies await this.initializeDependencies(); // Analyze target components const components = await this.identifyTargetComponents(config); if (components.length === 0) { throw new Error('No components found matching the criteria'); } console.log(chalk.gray(`Found ${components.length} component(s) for test generation`)); // Generate test plan const testPlan = await this.generateTestPlan(components, config); // Display test generation summary await this.displayTestGenerationSummary(testPlan); // Request confirmation const confirmed = await this.requestConfirmation(testPlan); if (!confirmed) { console.log(chalk.gray('Test generation cancelled')); return; } // Execute test generation const generationResults = await this.executeTestGeneration(testPlan); // Analyze generated tests const analysisResults = await this.analyzeGeneratedTests(generationResults); // Update existing test documentation await this.updateTestDocumentation(generationResults); // Display success summary console.log(chalk.green('\n✅ Test generation completed successfully')); console.log(chalk.gray(` Components tested: ${components.length}`)); console.log(chalk.gray(` Test files generated: ${generationResults.testFilesGenerated}`)); console.log(chalk.gray(` Expected coverage: ${analysisResults.expectedCoverage}%`)); console.log(chalk.gray(` Quality score: ${analysisResults.qualityScore}/10`)); return { success: true, components: components.length, testFilesGenerated: generationResults.testFilesGenerated, expectedCoverage: analysisResults.expectedCoverage, qualityScore: analysisResults.qualityScore }; } catch (error) { console.error(chalk.red(`\n❌ Test generation failed: ${error.message}`)); throw error; } } async parseParameters(params) { if (params.length < 2 && params[0] !== 'all') { throw new Error('Usage: *generate-tests <component-type> <component-name> [options]'); } const config = { componentType: params[0], componentName: params[1] || null, testType: 'unit', coverageTarget: 80, framework: 'jest', mockLevel: 'moderate', updateExisting: false, qualityLevel: 'standard', outputDir: null, generateAll: params[0] === 'all' }; // Parse options for (let i = 2; i < params.length; i++) { const param = params[i]; if (param === '--update-existing') { config.updateExisting = true; } else if (param.startsWith('--test-type') && params[i + 1]) { config.testType = params[++i]; } else if (param.startsWith('--coverage-target') && params[i + 1]) { config.coverageTarget = parseInt(params[++i]) || 80; } else if (param.startsWith('--framework') && params[i + 1]) { config.framework = params[++i]; } else if (param.startsWith('--mock-level') && params[i + 1]) { config.mockLevel = params[++i]; } else if (param.startsWith('--quality-level') && params[i + 1]) { config.qualityLevel = params[++i]; } else if (param.startsWith('--output-dir') && params[i + 1]) { config.outputDir = params[++i]; } } // Validation if (!config.generateAll) { const validTypes = ['agent', 'task', 'workflow', 'util']; if (!validTypes.includes(config.componentType)) { throw new Error(`Invalid component type: ${config.componentType}. Must be one of: ${validTypes.join(', ')}`); } } const validTestTypes = ['unit', 'integration', 'e2e', 'all']; if (!validTestTypes.includes(config.testType)) { throw new Error(`Invalid test type: ${config.testType}. Must be one of: ${validTestTypes.join(', ')}`); } const validFrameworks = ['jest', 'mocha', 'vitest']; if (!validFrameworks.includes(config.framework)) { throw new Error(`Invalid framework: ${config.framework}. Must be one of: ${validFrameworks.join(', ')}`); } const validMockLevels = ['minimal', 'moderate', 'extensive']; if (!validMockLevels.includes(config.mockLevel)) { throw new Error(`Invalid mock level: ${config.mockLevel}. Must be one of: ${validMockLevels.join(', ')}`); } const validQualityLevels = ['basic', 'standard', 'comprehensive']; if (!validQualityLevels.includes(config.qualityLevel)) { throw new Error(`Invalid quality level: ${config.qualityLevel}. Must be one of: ${validQualityLevels.join(', ')}`); } if (config.coverageTarget < 0 || config.coverageTarget > 100) { throw new Error('Coverage target must be between 0 and 100'); } return config; } async initializeDependencies() { try { // Initialize test template system const TestTemplateSystem = require('../scripts/test-template-system'); this.testTemplateSystem = new TestTemplateSystem({ rootPath: this.rootPath }); await this.testTemplateSystem.initialize(); // Initialize test generator const TestGenerator = require('../scripts/test-generator'); this.testGenerator = new TestGenerator({ rootPath: this.rootPath, templateSystem: this.testTemplateSystem }); // Initialize coverage analyzer const CoverageAnalyzer = require('../scripts/coverage-analyzer'); this.coverageAnalyzer = new CoverageAnalyzer({ rootPath: this.rootPath }); // Initialize quality assessment const TestQualityAssessment = require('../scripts/test-quality-assessment'); this.qualityAssessment = new TestQualityAssessment({ rootPath: this.rootPath }); } catch (error) { throw new Error(`Failed to initialize dependencies: ${error.message}`); } } async identifyTargetComponents(config) { const components = []; if (config.generateAll) { // Find all components in the framework const allComponents = await this.discoverAllComponents(); components.push(...allComponents); } else { // Find specific component const ComponentSearch = require('../scripts/component-search'); const componentSearch = new ComponentSearch({ rootPath: this.rootPath }); const component = await componentSearch.findComponent(config.componentType, config.componentName); if (!component) { // Suggest similar components const suggestions = await componentSearch.findSimilarComponents(config.componentType, config.componentName); if (suggestions.length > 0) { console.log(chalk.yellow('\nDid you mean one of these?')); suggestions.forEach(suggestion => { console.log(chalk.gray(` - ${suggestion.type}/${suggestion.name}`)); }); } throw new Error(`Component not found: ${config.componentType}/${config.componentName}`); } components.push(component); } return components; } async discoverAllComponents() { const components = []; const componentTypes = ['agents', 'tasks', 'workflows', 'utils']; for (const type of componentTypes) { const typeDir = path.join(this.rootPath, 'aios-core', type); try { const files = await fs.readdir(typeDir); for (const file of files) { const filePath = path.join(typeDir, file); const stats = await fs.stat(filePath); if (stats.isFile()) { const componentType = type.slice(0, -1); // Remove 's' from plural const componentName = path.basename(file, path.extname(file)); components.push({ id: `${componentType}/${componentName}`, type: componentType, name: componentName, filePath: filePath }); } } } catch (error) { // Directory doesn't exist, skip continue; } } return components; } async generateTestPlan(components, config) { const testPlan = { generation_id: `test-gen-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`, components: components, config: config, test_suites: [], estimated_duration: 0, expected_files: 0, quality_targets: {} }; console.log(chalk.gray('Analyzing components and generating test plan...')); for (const component of components) { const componentAnalysis = await this.analyzeComponentForTesting(component, config); const testSuite = await this.planTestSuite(component, componentAnalysis, config); testPlan.test_suites.push(testSuite); testPlan.estimated_duration += testSuite.estimated_duration; testPlan.expected_files += testSuite.test_files.length; } // Set quality targets testPlan.quality_targets = { coverage_target: config.coverageTarget, quality_level: config.qualityLevel, mock_coverage: this.calculateMockCoverage(config.mockLevel), assertion_density: this.calculateAssertionDensity(config.qualityLevel) }; return testPlan; } async analyzeComponentForTesting(component, config) { const analysis = { component_id: component.id, complexity: 'medium', dependencies: [], external_dependencies: [], testable_functions: [], edge_cases: [], mock_requirements: [], existing_tests: null }; try { // Read component file const content = await fs.readFile(component.filePath, 'utf-8'); // Analyze component complexity analysis.complexity = this.assessComplexity(content, component.type); // Identify dependencies analysis.dependencies = this.extractDependencies(content, component.type); analysis.external_dependencies = this.extractExternalDependencies(content); // Identify testable functions/methods analysis.testable_functions = this.extractTestableFunctions(content, component.type); // Identify edge cases analysis.edge_cases = this.identifyEdgeCases(content, component.type); // Determine mock requirements analysis.mock_requirements = this.determineMockRequirements(analysis, config.mockLevel); // Check for existing tests analysis.existing_tests = await this.findExistingTests(component); } catch (error) { console.warn(chalk.yellow(`Failed to analyze ${component.id}: ${error.message}`)); } return analysis; } async planTestSuite(component, analysis, config) { const testSuite = { component_id: component.id, component_type: component.type, test_files: [], total_tests: 0, estimated_duration: 0, coverage_plan: {}, quality_metrics: {} }; // Plan different types of tests based on config if (config.testType === 'unit' || config.testType === 'all') { const unitTestFile = await this.planUnitTests(component, analysis, config); testSuite.test_files.push(unitTestFile); } if (config.testType === 'integration' || config.testType === 'all') { const integrationTestFile = await this.planIntegrationTests(component, analysis, config); if (integrationTestFile) { testSuite.test_files.push(integrationTestFile); } } if (config.testType === 'e2e' || config.testType === 'all') { const e2eTestFile = await this.planE2ETests(component, analysis, config); if (e2eTestFile) { testSuite.test_files.push(e2eTestFile); } } // Calculate totals testSuite.total_tests = testSuite.test_files.reduce((sum, file) => sum + file.test_count, 0); testSuite.estimated_duration = testSuite.test_files.reduce((sum, file) => sum + file.estimated_duration, 0); // Plan coverage testSuite.coverage_plan = { target_percentage: config.coverageTarget, lines_to_cover: analysis.testable_functions.length * 5, // Estimate assertions_planned: testSuite.total_tests * this.calculateAssertionDensity(config.qualityLevel) }; return testSuite; } async planUnitTests(component, analysis, config) { const testFile = { file_path: this.generateTestFilePath(component, 'unit', config), test_type: 'unit', test_count: 0, estimated_duration: 0, test_cases: [], mocks_required: [], setup_requirements: [] }; // Generate test cases for each testable function for (const func of analysis.testable_functions) { const testCases = await this.generateFunctionTestCases(func, analysis, config); testFile.test_cases.push(...testCases); testFile.test_count += testCases.length; } // Add edge case tests for (const edgeCase of analysis.edge_cases) { const edgeCaseTest = await this.generateEdgeCaseTest(edgeCase, analysis, config); testFile.test_cases.push(edgeCaseTest); testFile.test_count++; } // Determine mocks required testFile.mocks_required = analysis.mock_requirements.filter(mock => mock.level !== 'none'); // Calculate estimated duration testFile.estimated_duration = testFile.test_count * 2; // 2 minutes per test case return testFile; } async planIntegrationTests(component, analysis, config) { // Skip integration tests for simple components if (analysis.complexity === 'low' && analysis.dependencies.length === 0) { return null; } const testFile = { file_path: this.generateTestFilePath(component, 'integration', config), test_type: 'integration', test_count: 0, estimated_duration: 0, test_cases: [], integration_scenarios: [], setup_requirements: ['test database', 'mock services'] }; // Generate integration scenarios testFile.integration_scenarios = await this.generateIntegrationScenarios(component, analysis); testFile.test_count = testFile.integration_scenarios.length; testFile.estimated_duration = testFile.test_count * 5; // 5 minutes per integration test return testFile; } async planE2ETests(component, analysis, config) { // Only generate E2E tests for workflows and certain tasks if (component.type !== 'workflow' && component.type !== 'task') { return null; } const testFile = { file_path: this.generateTestFilePath(component, 'e2e', config), test_type: 'e2e', test_count: 0, estimated_duration: 0, test_scenarios: [], setup_requirements: ['full system', 'test data'] }; // Generate E2E scenarios testFile.test_scenarios = await this.generateE2EScenarios(component, analysis); testFile.test_count = testFile.test_scenarios.length; testFile.estimated_duration = testFile.test_count * 10; // 10 minutes per E2E test return testFile; } generateTestFilePath(component, testType, config) { const baseDir = config.outputDir || path.join(this.rootPath, 'tests'); const typeDir = testType === 'unit' ? 'unit' : testType === 'integration' ? 'integration' : 'e2e'; const componentDir = component.type === 'util' ? 'utils' : component.type; const fileName = `${component.name}.${testType}.test.js`; return path.join(baseDir, typeDir, componentDir, fileName); } async displayTestGenerationSummary(testPlan) { console.log(chalk.blue('\n📋 Test Generation Plan')); console.log(chalk.gray('━'.repeat(50))); console.log(`Components: ${chalk.white(testPlan.components.length)}`); console.log(`Test suites: ${chalk.white(testPlan.test_suites.length)}`); console.log(`Expected test files: ${chalk.white(testPlan.expected_files)}`); console.log(`Total tests planned: ${chalk.white(testPlan.test_suites.reduce((sum, suite) => sum + suite.total_tests, 0))}`); console.log(`Coverage target: ${chalk.white(testPlan.config.coverageTarget)}%`); console.log(`Quality level: ${chalk.white(testPlan.config.qualityLevel)}`); console.log(`Estimated duration: ${chalk.white(Math.round(testPlan.estimated_duration / 60))} hours`); console.log(chalk.blue('\nTest Suites:')); testPlan.test_suites.forEach((suite, index) => { console.log(` ${index + 1}. ${suite.component_id} (${suite.total_tests} tests)`); }); } async requestConfirmation(testPlan) { const { confirmed } = await inquirer.prompt([{ type: 'confirm', name: 'confirmed', message: `Generate ${testPlan.expected_files} test files for ${testPlan.components.length} components?`, default: true }]); return confirmed; } async executeTestGeneration(testPlan) { const results = { testFilesGenerated: 0, testsGenerated: 0, errors: [], generated_files: [] }; console.log(chalk.gray('\nGenerating test files...')); for (const testSuite of testPlan.test_suites) { try { const component = testPlan.components.find(c => c.id === testSuite.component_id); for (const testFile of testSuite.test_files) { console.log(chalk.gray(` Generating ${testFile.test_type} tests for ${component.name}...`)); const generatedContent = await this.testGenerator.generateTestFile( component, testFile, testPlan.config ); // Ensure directory exists await fs.mkdir(path.dirname(testFile.file_path), { recursive: true }); // Write test file if (testPlan.config.updateExisting || !(await this.fileExists(testFile.file_path))) { await fs.writeFile(testFile.file_path, generatedContent); results.testFilesGenerated++; results.testsGenerated += testFile.test_count; results.generated_files.push(testFile.file_path); console.log(chalk.green(` Generated ${testFile.file_path}`)); } else { console.log(chalk.yellow(` Skipped ${testFile.file_path} (already exists)`)); } } } catch (error) { results.errors.push({ component_id: testSuite.component_id, error: error.message }); console.error(chalk.red(` Failed to generate tests for ${testSuite.component_id}: ${error.message}`)); } } return results; } async analyzeGeneratedTests(generationResults) { const analysis = { expectedCoverage: 0, qualityScore: 0, testDistribution: {}, recommendations: [] }; if (generationResults.testFilesGenerated > 0) { // Analyze each generated test file for (const filePath of generationResults.generated_files) { try { const testAnalysis = await this.qualityAssessment.analyzeSingleTestFile(filePath); analysis.expectedCoverage += testAnalysis.estimatedCoverage || 0; analysis.qualityScore += testAnalysis.qualityScore || 0; } catch (error) { console.warn(chalk.yellow(`Failed to analyze ${filePath}: ${error.message}`)); } } // Calculate averages analysis.expectedCoverage = Math.round(analysis.expectedCoverage / generationResults.testFilesGenerated); analysis.qualityScore = Math.round((analysis.qualityScore / generationResults.testFilesGenerated) * 10) / 10; } return analysis; } async updateTestDocumentation(generationResults) { // Update test documentation with newly generated tests const testDocs = { generated_at: new Date().toISOString(), test_files: generationResults.generated_files, total_tests: generationResults.testsGenerated, generation_notes: 'Auto-generated by AIOS test generation system' }; const docsPath = path.join(this.rootPath, 'docs', 'testing', 'auto-generated-tests.json'); try { await fs.mkdir(path.dirname(docsPath), { recursive: true }); await fs.writeFile(docsPath, JSON.stringify(testDocs, null, 2)); } catch (error) { console.warn(chalk.yellow(`Failed to update test documentation: ${error.message}`)); } } // Helper methods async fileExists(filePath) { try { await fs.access(filePath); return true; } catch { return false; } } assessComplexity(content, componentType) { // Simple heuristic for complexity assessment const lines = content.split('\n').length; const functions = (content.match(/function|=>/g) || []).length; const conditions = (content.match(/if|switch|for|while/g) || []).length; const complexityScore = (lines / 10) + (functions * 2) + (conditions * 3); if (complexityScore > 50) return 'high'; if (complexityScore > 20) return 'medium'; return 'low'; } extractDependencies(content, componentType) { const dependencies = []; // Extract require/import statements const requireMatches = content.match(/require\(['"]([^'"]+)['"]\)/g) || []; const importMatches = content.match(/import .+ from ['"]([^'"]+)['"]/g) || []; requireMatches.forEach(match => { const dep = match.match(/require\(['"]([^'"]+)['"]\)/)[1]; if (dep.startsWith('./') || dep.startsWith('../')) { dependencies.push(dep); } }); importMatches.forEach(match => { const dep = match.match(/from ['"]([^'"]+)['"]/)[1]; if (dep.startsWith('./') || dep.startsWith('../')) { dependencies.push(dep); } }); return dependencies; } extractExternalDependencies(content) { const externalDeps = []; const requireMatches = content.match(/require\(['"]([^'"]+)['"]\)/g) || []; const importMatches = content.match(/import .+ from ['"]([^'"]+)['"]/g) || []; requireMatches.forEach(match => { const dep = match.match(/require\(['"]([^'"]+)['"]\)/)[1]; if (!dep.startsWith('./') && !dep.startsWith('../') && !dep.startsWith('node:')) { externalDeps.push(dep); } }); importMatches.forEach(match => { const dep = match.match(/from ['"]([^'"]+)['"]/)[1]; if (!dep.startsWith('./') && !dep.startsWith('../') && !dep.startsWith('node:')) { externalDeps.push(dep); } }); return [...new Set(externalDeps)]; // Remove duplicates } extractTestableFunctions(content, componentType) { const functions = []; // Extract function declarations and expressions const functionMatches = content.match(/(?:function\s+(\w+)|(\w+)\s*[:=]\s*(?:async\s+)?function|(\w+)\s*[:=]\s*(?:async\s+)?\([^)]*\)\s*=>)/g) || []; functionMatches.forEach(match => { let functionName = null; if (match.includes('function ')) { functionName = match.match(/function\s+(\w+)/)?.[1]; } else { functionName = match.match(/(\w+)\s*[:=]/)?.[1]; } if (functionName && !functionName.startsWith('_')) { functions.push({ name: functionName, type: 'function', visibility: 'public' }); } }); return functions; } identifyEdgeCases(content, componentType) { const edgeCases = []; // Look for common edge case patterns if (content.includes('null') || content.includes('undefined')) { edgeCases.push({ type: 'null_undefined', description: 'Handle null/undefined values' }); } if (content.includes('length') || content.includes('size')) { edgeCases.push({ type: 'empty_collections', description: 'Handle empty arrays/objects' }); } if (content.includes('catch') || content.includes('throw')) { edgeCases.push({ type: 'error_handling', description: 'Test error conditions' }); } if (content.includes('async') || content.includes('await')) { edgeCases.push({ type: 'async_operations', description: 'Test async operation failures' }); } return edgeCases; } determineMockRequirements(analysis, mockLevel) { const mocks = []; if (mockLevel === 'minimal') { // Only mock external dependencies analysis.external_dependencies.forEach(dep => { mocks.push({ type: 'external', target: dep, level: 'full' }); }); } else if (mockLevel === 'moderate') { // Mock external dependencies and some internal ones analysis.external_dependencies.forEach(dep => { mocks.push({ type: 'external', target: dep, level: 'full' }); }); analysis.dependencies.slice(0, 3).forEach(dep => { mocks.push({ type: 'internal', target: dep, level: 'partial' }); }); } else if (mockLevel === 'extensive') { // Mock everything possible analysis.external_dependencies.forEach(dep => { mocks.push({ type: 'external', target: dep, level: 'full' }); }); analysis.dependencies.forEach(dep => { mocks.push({ type: 'internal', target: dep, level: 'full' }); }); } return mocks; } async findExistingTests(component) { const possibleTestPaths = [ path.join(this.rootPath, 'tests', 'unit', component.type, `${component.name}.test.js`), path.join(this.rootPath, 'tests', 'unit', component.type, `${component.name}.spec.js`), path.join(this.rootPath, 'test', `${component.name}.test.js`), path.join(this.rootPath, '__tests__', `${component.name}.test.js`) ]; for (const testPath of possibleTestPaths) { if (await this.fileExists(testPath)) { return { path: testPath, exists: true }; } } return { exists: false }; } calculateMockCoverage(mockLevel) { const coverage = { minimal: 30, moderate: 60, extensive: 90 }; return coverage[mockLevel] || 60; } calculateAssertionDensity(qualityLevel) { const density = { basic: 2, standard: 4, comprehensive: 6 }; return density[qualityLevel] || 4; } async generateFunctionTestCases(func, analysis, config) { // This would generate test cases based on function analysis // For now, return basic test case structure return [ { name: `should ${func.name} successfully`, type: 'success_case', description: `Test successful execution of ${func.name}`, assertions: config.qualityLevel === 'comprehensive' ? 3 : 2 }, { name: `should handle ${func.name} errors`, type: 'error_case', description: `Test error handling in ${func.name}`, assertions: 2 } ]; } async generateEdgeCaseTest(edgeCase, analysis, config) { return { name: `should handle ${edgeCase.type}`, type: 'edge_case', description: edgeCase.description, assertions: 1 }; } async generateIntegrationScenarios(component, analysis) { // Generate integration test scenarios based on component dependencies const scenarios = []; if (analysis.dependencies.length > 0) { scenarios.push({ name: 'should integrate with dependencies', description: 'Test integration with internal dependencies', dependencies: analysis.dependencies.slice(0, 3) }); } if (analysis.external_dependencies.length > 0) { scenarios.push({ name: 'should integrate with external services', description: 'Test integration with external dependencies', dependencies: analysis.external_dependencies.slice(0, 2) }); } return scenarios; } async generateE2EScenarios(component, analysis) { const scenarios = []; if (component.type === 'workflow') { scenarios.push({ name: 'should complete full workflow', description: 'Test complete workflow execution end-to-end', steps: ['initialize', 'execute', 'validate', 'cleanup'] }); } if (component.type === 'task') { scenarios.push({ name: 'should execute task end-to-end', description: 'Test complete task execution with real dependencies', steps: ['setup', 'execute', 'verify_output'] }); } return scenarios; } } module.exports = GenerateTestsTask; ``` ## Validation Rules ### Input Validation - Component type must be valid or 'all' for bulk generation - Coverage target must be 0-100% - Test framework must be supported - Quality level must be recognized ### Safety Checks - Don't overwrite existing tests without confirmation - Validate component exists before test generation - Ensure output directory is writable - Check for conflicting test frameworks ### Generation Requirements - Must generate syntactically valid test code - Should include appropriate setup/teardown - Must include meaningful test descriptions - Should follow testing best practices ## Integration Points ### Test Template System - Provides reusable test templates - Manages test code generation patterns - Handles framework-specific syntax - Supports custom test structures ### Test Generator - Orchestrates test file creation - Analyzes components for testable elements - Generates comprehensive test suites - Handles different test types (unit, integration, e2e) ### Coverage Analyzer - Analyzes existing test coverage - Identifies coverage gaps - Provides coverage metrics - Suggests coverage improvements ### Quality Assessment - Evaluates generated test quality - Provides quality metrics and scores - Identifies test improvement opportunities - Validates test effectiveness ## Output Structure ### Success Response ```json { "success": true, "components": 5, "testFilesGenerated": 12, "expectedCoverage": 85, "qualityScore": 8.5 } ``` ### Error Response ```json { "success": false, "error": "Component not found: agent/invalid-name", "suggestions": ["weather-agent", "data-agent"] } ``` ## Security Considerations - Validate all file paths to prevent directory traversal - Sanitize component names and test descriptions - Ensure generated code doesn't include sensitive information - Validate test framework dependencies for security vulnerabilities