Steuerkram/tests/run.php

145 lines
4.6 KiB
PHP

#!/usr/bin/env php
<?php
declare(strict_types=1);
/**
* Steuerkram Test-Runner
*
* Führt alle Tests im tests/-Verzeichnis aus und gibt eine Zusammenfassung aus.
* Exit-Code: 0 = alle Tests bestanden, 1 = mindestens ein Fehler.
*
* Verwendung:
* php tests/run.php
* php tests/run.php --json # Ausgabe als JSON (für Skript-Auswertung)
*/
$projectRoot = dirname(__DIR__);
$autoload = $projectRoot . '/vendor/autoload.php';
if (!file_exists($autoload)) {
fwrite(STDERR, "Fehler: vendor/autoload.php nicht gefunden. Bitte 'composer install' ausführen.\n");
exit(1);
}
require $autoload;
require __DIR__ . '/TestCase.php';
\Steuerkram\Config::load();
$jsonMode = in_array('--json', $argv ?? [], true);
/**
* Erfasst den Bestand unterhalb der Datenverzeichnisse.
*
* Ein Vergleich vor/nach dem Lauf deckt Rückstände auf, die ein Test nicht über
* TestCase::registerTempPath() angemeldet hat. Solche Reste sind gefährlich,
* weil der Rollback die Belegnummer wieder freigibt: ein späterer echter Upload
* trifft dann auf ein bereits existierendes Verzeichnis.
*/
function datenBestand(): array
{
$bestand = [];
foreach ([\Steuerkram\Config::get('upload_dir'), \Steuerkram\Config::get('logo_dir')] as $root) {
if (!is_string($root) || !is_dir($root)) continue;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $entry) {
$bestand[$entry->getPathname()] = true;
}
}
return $bestand;
}
/**
* Liefert die neu hinzugekommenen Pfade, ohne deren Unterpfade (sonst meldet
* ein neues Verzeichnis auch jede darin liegende Datei einzeln).
*/
function neuePfade(array $vorher, array $nachher): array
{
$neu = array_keys(array_diff_key($nachher, $vorher));
sort($neu);
$oberste = [];
foreach ($neu as $pfad) {
foreach ($oberste as $bekannt) {
if (str_starts_with($pfad, $bekannt . '/')) continue 2;
}
$oberste[] = $pfad;
}
return $oberste;
}
// Alle Test-Dateien laden
$testFiles = glob(__DIR__ . '/[0-9][0-9]_*.php');
sort($testFiles);
$testClasses = [];
foreach ($testFiles as $file) {
require_once $file;
// Klassenname aus Dateiname ableiten: 01_BelegUploadTest.php → BelegUploadTest
$base = basename($file, '.php');
$className = preg_replace('/^\d+_/', '', $base);
if (class_exists($className)) {
$testClasses[] = $className;
}
}
if (empty($testClasses)) {
fwrite(STDERR, "Keine Testklassen gefunden.\n");
exit(1);
}
if (!$jsonMode) {
echo "\n\e[1m╔══════════════════════════════════════════╗\e[0m\n";
echo "\e[1m║ Steuerkram Test-Suite ║\e[0m\n";
echo "\e[1m╚══════════════════════════════════════════╝\e[0m\n";
}
$allFailures = [];
$dataVorher = datenBestand();
foreach ($testClasses as $class) {
/** @var TestCase $test */
$test = new $class();
$failures = $test->execute();
$allFailures = array_merge($allFailures, $failures);
}
$summary = TestCase::globalSummary();
$rueckstaende = neuePfade($dataVorher, datenBestand());
if (!$jsonMode) {
$total = $summary['passed'] + $summary['failed'];
echo "\n\e[1m══════════════════════════════════════════\e[0m\n";
if ($summary['failed'] === 0) {
echo "\e[32;1m✓ Alle {$summary['passed']} Tests bestanden.\e[0m\n";
} else {
echo "\e[31;1m✗ {$summary['failed']} von {$total} Tests fehlgeschlagen:\e[0m\n";
foreach ($summary['failures'] as $failure) {
echo " • {$failure}\n";
}
}
echo "\e[1m══════════════════════════════════════════\e[0m\n";
if ($rueckstaende) {
echo "\n\e[33mHinweis: Der Testlauf hat Dateien in den Datenverzeichnissen hinterlassen:\e[0m\n";
foreach ($rueckstaende as $pfad) {
echo " • {$pfad}\n";
}
echo " Der zuständige Test sollte den Pfad über \e[1m\$this->registerTempPath()\e[0m anmelden.\n";
}
echo "\n";
} else {
echo json_encode([
'passed' => $summary['passed'],
'failed' => $summary['failed'],
'failures' => $summary['failures'],
], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
echo "\n";
}
exit($summary['failed'] > 0 ? 1 : 0);