153 lines
5 KiB
PHP
153 lines
5 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);
|
||
TestCase::setQuiet($jsonMode);
|
||
|
||
// PHP-Warnungen/-Notices einzelner Tests (z. B. absichtlich provozierte
|
||
// Fehlerfälle) dürfen die JSON-Ausgabe auf STDOUT nicht verunreinigen –
|
||
// scripts/container/run-tests.sh wertet sie sonst nicht mehr auswertbar aus.
|
||
if ($jsonMode) {
|
||
ini_set('display_errors', 'stderr');
|
||
}
|
||
|
||
/**
|
||
* 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);
|