Files
rspade_system/app/RSpade/Commands/Rsx/Rsx_Test_Command.php
root 29c657f7a7 Exclude tests directory from framework publish
Add 100+ automated unit tests from .expect file specifications
Add session system test
Add rsx:constants:regenerate command test
Add rsx:logrotate command test
Add rsx:clean command test
Add rsx:manifest:stats command test
Add model enum system test
Add model mass assignment prevention test
Add rsx:check command test
Add migrate:status command test

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-25 03:59:58 +00:00

163 lines
4.8 KiB
PHP

<?php
/**
* CODING CONVENTION:
* This file follows the coding convention where variable_names and function_names
* use snake_case (underscore_wherever_possible).
*/
namespace App\RSpade\Commands\Rsx;
use App\Console\Commands\FrameworkDeveloperCommand;
use App\RSpade\Core\Manifest\Manifest;
use Exception;
use ReflectionClass;
class Rsx_Test_Command extends FrameworkDeveloperCommand
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'rsx:test
{test? : Specific test class to run}
{--filter= : Run only tests matching this pattern}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Run RSX framework tests';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$specific_test = $this->argument('test');
$filter = $this->option('filter');
$this->info('RSX Test Runner');
$this->info('================');
$this->newLine();
// Rebuild manifest to ensure we have latest test classes
$this->line('Building manifest...');
Manifest::init();
// Get all test classes extending Rsx_Test_Abstract
$test_classes = Manifest::php_get_extending('Rsx_Test_Abstract');
if (empty($test_classes)) {
$this->warn('No test classes found.');
$this->line('Test classes should extend App\\RSpade\\Core\\Testing\\Rsx_Test_Abstract');
return 0;
}
$total_tests = 0;
$total_passed = 0;
$total_failed = 0;
$total_skipped = 0;
foreach ($test_classes as $test_class_info) {
if (!isset($test_class_info['fqcn'])) {
continue;
}
$class_name = $test_class_info['fqcn'];
// Skip if specific test requested and this isn't it
if ($specific_test && !str_contains($class_name, $specific_test)) {
continue;
}
// Skip abstract classes
$reflection = new ReflectionClass($class_name);
// @PHP-REFLECT-01-EXCEPTION - Test runner needs ReflectionClass for filtering
if ($reflection->isAbstract()) {
continue;
}
$short_name = basename(str_replace('\\', '/', $class_name));
$this->info("Running: {$short_name}");
try {
// Run the tests
$results = $class_name::run();
// Process results
foreach ($results as $test_name => $result) {
// Apply filter if provided
if ($filter && !str_contains($test_name, $filter)) {
continue;
}
$total_tests++;
switch ($result['status']) {
case 'passed':
$total_passed++;
$this->line("{$test_name}");
break;
case 'failed':
$total_failed++;
$this->error("{$test_name}");
$this->line(" {$result['message']}", 'fg=red');
if (isset($result['file']) && isset($result['line'])) {
$this->line(" at {$result['file']}:{$result['line']}", 'fg=gray');
}
break;
case 'skipped':
$total_skipped++;
$this->line(" - {$test_name} (skipped)", 'fg=yellow');
$this->line(" {$result['message']}", 'fg=gray');
break;
}
}
} catch (Exception $e) {
$this->error(' Error running test class: ' . $e->getMessage());
$total_failed++;
}
$this->newLine();
}
// Summary
$this->info('Test Summary');
$this->info('============');
$this->line("Total: {$total_tests}");
if ($total_passed > 0) {
$this->line("Passed: {$total_passed}", 'fg=green');
}
if ($total_failed > 0) {
$this->line("Failed: {$total_failed}", 'fg=red');
}
if ($total_skipped > 0) {
$this->line("Skipped: {$total_skipped}", 'fg=yellow');
}
$this->newLine();
if ($total_failed > 0) {
$this->error('Tests failed!');
return 1;
}
if ($total_tests === 0) {
$this->warn('No tests were run.');
return 0;
}
$this->info('All tests passed!');
return 0;
}
}