[ 'name' => 'Contract Tests', 'description' => 'API endpoint validation and database schema contracts', 'required' => true, 'order' => 1 ], 'integration' => [ 'name' => 'Integration Tests', 'description' => 'Synchronization workflows and external service integration', 'required' => true, 'order' => 2 ], 'security' => [ 'name' => 'Security Tests', 'description' => 'Encryption, authentication, and vulnerability testing', 'required' => true, 'order' => 3 ], 'performance' => [ 'name' => 'Performance Tests', 'description' => 'Queue processing, rate limiting, and benchmark testing', 'required' => true, 'order' => 4 ], 'unit' => [ 'name' => 'Unit Tests', 'description' => 'Business logic validation and isolated component testing', 'required' => true, 'order' => 5 ], 'e2e' => [ 'name' => 'End-to-End Tests', 'description' => 'Complete user workflow validation', 'required' => true, 'order' => 6 ], 'database' => [ 'name' => 'Database Tests', 'description' => 'Database schema and constraint validation', 'required' => true, 'order' => 0 ] ]; private array $config; private bool $strictTDD; private bool $continueOnFailure; public function __construct(array $config = []) { $this->config = array_merge([ 'strict_tdd' => true, 'continue_on_failure' => false, 'coverage_threshold' => 100, 'performance_benchmarks' => true, 'security_audits' => true, 'real_api_testing' => true, 'parallel_execution' => false ], $config); $this->strictTDD = $this->config['strict_tdd']; $this->continueOnFailure = $this->config['continue_on_failure']; } public function runFullSuite(): array { echo "\n" . str_repeat("=", 80) . "\n"; echo "DESK-MOLONI v3.0 TDD TEST SUITE\n"; echo "Following Test-Driven Development Methodology\n"; echo str_repeat("=", 80) . "\n\n"; if ($this->strictTDD) { echo "🔴 STRICT TDD MODE: All tests MUST FAIL initially\n"; echo "🟢 Implementation only begins after RED phase\n\n"; } $results = [ 'suites' => [], 'overall_success' => true, 'total_tests' => 0, 'total_failures' => 0, 'total_errors' => 0, 'total_skipped' => 0, 'execution_time' => 0, 'coverage_percentage' => 0, 'tdd_compliance' => true ]; $startTime = microtime(true); // Sort test suites by order uasort($this->testSuites, function($a, $b) { return $a['order'] <=> $b['order']; }); foreach ($this->testSuites as $suite => $config) { echo "🧪 Running {$config['name']}...\n"; echo " {$config['description']}\n"; $suiteResult = $this->runTestSuite($suite); $results['suites'][$suite] = $suiteResult; // Aggregate results $results['total_tests'] += $suiteResult['tests']; $results['total_failures'] += $suiteResult['failures']; $results['total_errors'] += $suiteResult['errors']; $results['total_skipped'] += $suiteResult['skipped']; // Check TDD compliance if ($this->strictTDD && $suiteResult['success']) { echo " ⚠️ WARNING: Tests should FAIL in TDD red phase!\n"; $results['tdd_compliance'] = false; } if (!$suiteResult['success']) { $results['overall_success'] = false; if (!$this->continueOnFailure && $config['required']) { echo " ❌ Critical test suite failed. Stopping execution.\n"; break; } } $this->displaySuiteResults($suiteResult); echo "\n"; } $results['execution_time'] = microtime(true) - $startTime; // Generate coverage report if ($this->config['coverage_threshold'] > 0) { $results['coverage_percentage'] = $this->generateCoverageReport(); } $this->displayOverallResults($results); return $results; } private function runTestSuite(string $suite): array { $command = sprintf( 'cd %s && vendor/bin/phpunit --testsuite %s --log-junit reports/%s-junit.xml', escapeshellarg(dirname(__DIR__)), escapeshellarg($suite), escapeshellarg($suite) ); if ($this->config['coverage_threshold'] > 0) { $command .= sprintf(' --coverage-clover reports/%s-coverage.xml', escapeshellarg($suite)); } $output = []; $returnCode = 0; exec($command . ' 2>&1', $output, $returnCode); return $this->parseTestOutput($output, $returnCode); } private function parseTestOutput(array $output, int $returnCode): array { $result = [ 'success' => $returnCode === 0, 'tests' => 0, 'failures' => 0, 'errors' => 0, 'skipped' => 0, 'time' => 0.0, 'output' => implode("\n", $output) ]; foreach ($output as $line) { // Parse PHPUnit output if (preg_match('/Tests: (\d+), Assertions: \d+/', $line, $matches)) { $result['tests'] = (int)$matches[1]; } if (preg_match('/Failures: (\d+)/', $line, $matches)) { $result['failures'] = (int)$matches[1]; } if (preg_match('/Errors: (\d+)/', $line, $matches)) { $result['errors'] = (int)$matches[1]; } if (preg_match('/Skipped: (\d+)/', $line, $matches)) { $result['skipped'] = (int)$matches[1]; } if (preg_match('/Time: ([0-9.]+)/', $line, $matches)) { $result['time'] = (float)$matches[1]; } } return $result; } private function displaySuiteResults(array $result): void { $status = $result['success'] ? '✅ PASSED' : '❌ FAILED'; $testsInfo = "Tests: {$result['tests']}, Failures: {$result['failures']}, Errors: {$result['errors']}"; if ($result['skipped'] > 0) { $testsInfo .= ", Skipped: {$result['skipped']}"; } echo " {$status} - {$testsInfo} (Time: {$result['time']}s)\n"; if (!$result['success']) { $errorLines = array_filter(explode("\n", $result['output']), function($line) { return strpos($line, 'FAIL') !== false || strpos($line, 'ERROR') !== false; }); foreach (array_slice($errorLines, 0, 3) as $errorLine) { echo " 💥 " . trim($errorLine) . "\n"; } } } private function generateCoverageReport(): float { echo "📊 Generating code coverage report...\n"; $command = sprintf( 'cd %s && vendor/bin/phpunit --testsuite unit,integration --coverage-html coverage --coverage-text', escapeshellarg(dirname(__DIR__)) ); $output = []; exec($command . ' 2>&1', $output); // Parse coverage percentage from output $coveragePercentage = 0; foreach ($output as $line) { if (preg_match('/Lines:\s+([0-9.]+)%/', $line, $matches)) { $coveragePercentage = (float)$matches[1]; break; } } return $coveragePercentage; } private function displayOverallResults(array $results): void { echo str_repeat("=", 80) . "\n"; echo "OVERALL TEST RESULTS\n"; echo str_repeat("=", 80) . "\n"; $status = $results['overall_success'] ? '✅ PASSED' : '❌ FAILED'; echo "Status: {$status}\n"; echo "Total Tests: {$results['total_tests']}\n"; echo "Failures: {$results['total_failures']}\n"; echo "Errors: {$results['total_errors']}\n"; echo "Skipped: {$results['total_skipped']}\n"; echo "Execution Time: " . number_format($results['execution_time'], 2) . "s\n"; if ($results['coverage_percentage'] > 0) { $coverageStatus = $results['coverage_percentage'] >= $this->config['coverage_threshold'] ? '✅' : '❌'; echo "Code Coverage: {$coverageStatus} {$results['coverage_percentage']}% (Target: {$this->config['coverage_threshold']}%)\n"; } // TDD Compliance Check if ($this->strictTDD) { $tddStatus = $results['tdd_compliance'] ? '❌ NOT COMPLIANT' : '✅ COMPLIANT'; echo "TDD Compliance: {$tddStatus}\n"; if (!$results['tdd_compliance']) { echo "\n⚠️ TDD VIOLATION: Some tests passed when they should fail in RED phase\n"; echo "🔴 All tests must FAIL before implementation begins\n"; } else { echo "\n🔴 Perfect! All tests failed as expected in TDD RED phase\n"; echo "🟢 Now proceed with implementation to make tests pass\n"; } } echo "\n"; $this->displayNextSteps($results); } private function displayNextSteps(array $results): void { echo "NEXT STEPS:\n"; echo str_repeat("-", 40) . "\n"; if ($this->strictTDD && $results['tdd_compliance']) { echo "1. ✅ RED phase complete - All tests are failing as expected\n"; echo "2. 🟢 Begin GREEN phase - Implement minimal code to make tests pass\n"; echo "3. 🔵 REFACTOR phase - Improve code quality while keeping tests green\n"; echo "4. 🔄 Repeat TDD cycle for next feature\n"; } elseif (!$results['overall_success']) { // Analyze which implementations are needed $failedSuites = array_filter($results['suites'], function($suite) { return !$suite['success']; }); echo "Required implementations based on failing tests:\n"; foreach ($failedSuites as $suiteName => $suite) { echo " • {$this->testSuites[$suiteName]['name']}\n"; } } else { echo "1. ✅ All tests are passing\n"; echo "2. 📊 Review code coverage report\n"; echo "3. 🔍 Consider additional edge cases\n"; echo "4. 📝 Update documentation\n"; } echo "\nTest reports available at:\n"; echo " • HTML Coverage: " . dirname(__DIR__) . "/coverage/index.html\n"; echo " • JUnit Reports: " . dirname(__DIR__) . "/tests/reports/\n"; } } // Run the test suite if (basename(__FILE__) === basename($_SERVER['SCRIPT_NAME'])) { $config = [ 'strict_tdd' => isset($_SERVER['argv']) && in_array('--strict-tdd', $_SERVER['argv']), 'continue_on_failure' => isset($_SERVER['argv']) && in_array('--continue', $_SERVER['argv']), 'coverage_threshold' => 100, 'real_api_testing' => !isset($_SERVER['argv']) || !in_array('--no-api', $_SERVER['argv']) ]; $runner = new TDDTestRunner($config); $results = $runner->runFullSuite(); // Exit with appropriate code exit($results['overall_success'] ? 0 : 1); }