FIX Teszt párhuzamos futtatás processzenkénti adatbázissal

A --parallel eddig két okból nem működött volna:

1. A setUp() a parent::setUp() ELŐTT hívja a refreshApplication()-t, hogy a DB
   nevét a RefreshDatabase előtt ellenőrizhesse. A Laravel viszont a
   ParallelTesting::callSetUpTestCaseCallbacks()-ot csak az "if (! $this->app)"
   ágban futtatja (InteractsWithTestCaseLifecycle), ami így kimaradt - a
   processzenkénti adatbázis sosem jött létre, minden processz a közös
   d2dTest-et migrálta volna egyszerre. A callbacket most magunk hívjuk meg;
   nem párhuzamos futásnál no-op.

2. A névellenőrzés pontos "d2dtest" egyezést várt, és eltérésnél nem hibát
   dobott, hanem VISSZAÍRTA a configot d2dTest-re - vagyis csendben közös
   adatbázisra terelte volna a processzeket.

A név mostantól a /^d2dtest(_test_\d+)?$/ mintát követi, ami a Laravel
TestDatabases::testDatabase() elnevezését fedi le. Szándékosan nem
str_starts_with($db, 'd2d'): az átengedné az éles adatbázist is. Az éles d2d
elleni azonnali exit(1) változatlan.

Ellenőrizve: tiszta lapról a párhuzamos futás létrehozta a d2dtest_test_1..6
adatbázisokat, a sima futás változatlanul működik.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
E98Developer 2026-08-17 10:34:13 +02:00
parent 11918b608d
commit 947eccff90

View File

@ -3,6 +3,7 @@
namespace Tests; namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase; use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Support\Facades\ParallelTesting;
abstract class TestCase extends BaseTestCase abstract class TestCase extends BaseTestCase
{ {
@ -10,14 +11,34 @@ protected function setUp(): void
{ {
if (! $this->app) { if (! $this->app) {
$this->refreshApplication(); $this->refreshApplication();
// A Laravel ezt a saját setUp()-jában csak akkor hívja meg, ha ŐT hozta létre az
// alkalmazást (InteractsWithTestCaseLifecycle: "if (! $this->app)"). Mi viszont
// előbb bootolunk - hogy a DB nevét a RefreshDatabase ELŐTT ellenőrizhessük -,
// ezért ott az az ág kimaradna, és a --parallel processzenkénti adatbázisa sosem
// jönne létre: minden processz a közös d2dTest-et migrálná egyszerre.
// Nem párhuzamos futásnál ez no-op (whenRunningInParallel).
ParallelTesting::callSetUpTestCaseCallbacks($this);
} }
$currentDb = config('database.connections.mysql.database'); $this->guardTestDatabase();
$currentEnv = env('APP_ENV');
parent::setUp();
}
/**
* Megakadályozza, hogy a tesztek az éles adatbázison fussanak.
*
* A parent::setUp() ELŐTT kell lefutnia: a RefreshDatabase feltétel nélkül eldobja
* a táblákat, tehát utólag ellenőrizni már késő lenne.
*/
private function guardTestDatabase(): void
{
$database = (string) config('database.connections.mysql.database');
// SZIGORÚ ELLENŐRZÉS: A 'd2d' adatbázist SOHA nem szabad teszthez használni! // SZIGORÚ ELLENŐRZÉS: A 'd2d' adatbázist SOHA nem szabad teszthez használni!
// Akkor is védünk, ha a config cache-elve van vagy az APP_ENV nem 'testing'. // Akkor is védünk, ha a config cache-elve van vagy az APP_ENV nem 'testing'.
if (strtolower($currentDb) === 'd2d') { if (strtolower($database) === 'd2d') {
echo "\n\n!!! KRITIKUS BIZTONSÁGI HIBA !!!\n"; echo "\n\n!!! KRITIKUS BIZTONSÁGI HIBA !!!\n";
echo "A tesztek az ÉLŐ (d2d) adatbázishoz kapcsolódnak!\n"; echo "A tesztek az ÉLŐ (d2d) adatbázishoz kapcsolódnak!\n";
echo "Ez az adatbázis törléséhez (RefreshDatabase) vezetne.\n"; echo "Ez az adatbázis törléséhez (RefreshDatabase) vezetne.\n";
@ -26,17 +47,31 @@ protected function setUp(): void
exit(1); exit(1);
} }
if (env('APP_ENV') !== 'testing') {
return;
}
// Kényszerítsük a teszt adatbázist, ha testing környezetben vagyunk és nem d2d // Kényszerítsük a teszt adatbázist, ha testing környezetben vagyunk és nem d2d
if ($currentEnv === 'testing' && strtolower($currentDb) !== 'd2dtest') { if (! $this->isTestDatabase($database)) {
config(['database.connections.mysql.database' => 'd2dTest']); config(['database.connections.mysql.database' => 'd2dTest']);
$currentDb = 'd2dTest'; $database = (string) config('database.connections.mysql.database');
} }
// Általános ellenőrzés, hogy a teszt adatbázis legyen használva // Általános ellenőrzés, hogy a teszt adatbázis legyen használva
if (strtolower($currentDb) !== 'd2dtest' && $currentEnv === 'testing') { if (! $this->isTestDatabase($database)) {
throw new \Exception('HIBA: A teszteknek a d2dTest adatbázison kellene futniuk. Aktuális konfiguráció: ' . $currentDb . '. Kérlek futtasd a "php artisan config:clear" parancsot!'); throw new \Exception('HIBA: A teszteknek a d2dTest adatbázison kellene futniuk. Aktuális konfiguráció: '.$database.'. Kérlek futtasd a "php artisan config:clear" parancsot!');
} }
}
parent::setUp(); /**
* Elfogadható a 'd2dTest', illetve a --parallel processzenkénti 'd2dTest_test_1' alakja
* (a nevet a Laravel adja: TestDatabases::testDatabase() -> "{$database}_test_{$token}").
*
* Szándékosan NEM egyszerű prefix-vizsgálat: a kézenfekvő str_starts_with($db, 'd2d')
* átengedné az éles 'd2d' nevet is.
*/
private function isTestDatabase(string $database): bool
{
return preg_match('/^d2dtest(_test_\d+)?$/', strtolower($database)) === 1;
} }
} }