d2d.emegrendeles.hu/tests/Feature/Deployment/DeploymentTestHelpers.php
E98Developer 98cf955354 FIX DeploymentPackageTest felbontása hat fájlra a párhuzamos futtatáshoz
A paratest FÁJLONKÉNT osztja szét a munkát, ezért egyetlen 28 tesztes fájlból
nem tudott párhuzamosítani: egy processz vitte a teljes 137 mp-et, a másik öt
üresjáratban állt. A --parallel emiatt korábban egyenesen lassabb volt
(136 -> 143 mp), a teljes suite-on pedig csak 11%-ot hozott.

A felosztás témák szerint történt, de mért időkre kalibrálva, hogy egyik fájl
se domináljon (a leghosszabb a Builder, ~35 mp):

  AccessTest    6 teszt  ~7 mp   jogosultság, feature flag, 403
  GitTest       5 teszt ~18 mp   GitRepository, ChangeSetAnalyzer
  BuilderTest   9 teszt ~35 mp   csomagépítés, _TEENDOK, _torles.sh, verzió
  PageTest      4 teszt ~21 mp   commitlista, keresés, kiválasztás
  CreationTest  2 teszt ~32 mp   felületről indított csomagolás, kirakottnak jelölés
  DownloadTest  2 teszt ~25 mp   naplóbejegyzés, ZIP letöltés, útvonal-ellenőrzés

A két legdrágább teszt (21,7 és 20,4 mp) szándékosan külön fájlba került,
együtt hagyva ők adnák a padlót.

A közös segédfüggvények a DeploymentTestHelpers.php-ba kerültek. Ez nem
*Test.php, ezért a phpunit nem szedi fel teszt-fájlként. A minta-repót takarító
afterAll() helyére register_shutdown_function lépett: az afterAll fájlonként
futna le, és a következő fájl alól húzná ki a mintát.

Mért eredmény (6 mag): ezek a tesztek sorosan 137,00 mp, párhuzamosan
67,88 mp. Teljes suite párhuzamosan 149,78 -> 77,52 mp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 10:48:31 +02:00

182 lines
6.1 KiB
PHP

<?php
/**
* A deployment csomagoló tesztjeinek közös segédfüggvényei.
*
* A tesztek több fájlra vannak bontva, hogy a `--parallel` szét tudja osztani őket
* (a paratest FÁJLONKÉNT oszt, egyetlen nagy fájlból nem tud párhuzamosítani).
* Ez a fájl nem `*Test.php`, ezért a phpunit nem szedi fel teszt-fájlként.
*/
use App\Models\FeatureFlag;
use App\Models\Role;
use App\Models\User;
use App\Services\FeatureFlagRegistrar;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\File;
use Symfony\Component\Process\Process;
/**
* Fejlesztői gépet szimuláló környezet: az .env kapcsoló és a stage whitelist rendben.
*/
function deploymentAllowEnvironment(): void
{
Config::set('deployment.enabled', true);
Config::set('app.stage', 'local');
}
function deploymentDeveloper(bool $flagEnabled = true): User
{
$role = Role::firstOrCreate(['name' => 'developer'], ['display_name' => 'Developer']);
$user = User::factory()->create();
$user->addRole($role);
FeatureFlag::create([
'name' => 'DeploymentPackage',
'label' => 'Deployment csomagoló',
'enabled' => $flagEnabled,
'stages' => null,
'roles' => ['developer'],
]);
app(FeatureFlagRegistrar::class)->registerAll();
return $user;
}
/**
* @param array<int, string> $arguments
*/
function deploymentGit(string $path, array $arguments): string
{
$process = new Process(array_merge(['git'], $arguments), $path);
$process->mustRun();
return $process->getOutput();
}
/**
* A minta-repó útvonala. A PID azért van benne, hogy párhuzamos futásnál (paratest)
* minden processznek saját mintája legyen.
*/
function deploymentTemplatePath(): string
{
return sys_get_temp_dir().DIRECTORY_SEPARATOR.'deployment-package-minta-'.getmypid();
}
/**
* Determinisztikus minta-repó: átnevezés, törlés, ékezetes fájlnév és migráció is van benne.
*
* A 12 git parancs processzenként EGYSZER fut le - Windowson a processzindítás olyan drága,
* hogy tesztenként újraépítve ez önmagában ~4 mp lenne. A tesztek másolatot kapnak.
*
* @return array{path:string, commits:array<int, string>}
*/
function deploymentRepositoryTemplate(): array
{
static $template = null;
// Ha a minta időközben eltűnt (kézi takarítás, megszakadt futás), újraépítjük.
if ($template !== null && is_dir($template['path'])) {
return $template;
}
$elsoFelepites = $template === null;
$path = deploymentTemplatePath();
// Egy korábbi, megszakadt futás azonos PID-ű maradványa nem szennyezheti a mintát.
deploymentDeleteDirectory($path);
File::makeDirectory($path.'/app', 0777, true);
File::makeDirectory($path.'/database/migrations', 0777, true);
File::makeDirectory($path.'/database/seeders', 0777, true);
deploymentGit($path, ['init']);
deploymentGit($path, ['config', 'user.email', 'teszt@example.com']);
deploymentGit($path, ['config', 'user.name', 'Teszt']);
deploymentGit($path, ['config', 'commit.gpgsign', 'false']);
File::put($path.'/app/Elso.php', "<?php\n// elso\n");
File::put($path.'/app/Regi.php', "<?php\n// regi\n");
File::put($path.'/app/Atnevezendo.php', "<?php\n// atnevezendo\n");
deploymentGit($path, ['add', '-A']);
deploymentGit($path, ['commit', '-m', 'első commit']);
deploymentGit($path, ['branch', '-M', 'main']);
File::put($path.'/app/Elso.php', "<?php\n// elso modositva\n");
File::put($path.'/app/Árlista.php', "<?php\n// ekezetes\n");
File::put($path.'/database/migrations/2026_01_01_000000_teszt.php', "<?php\n// migracio\n");
File::put($path.'/database/seeders/TesztSeeder.php', "<?php\n// seeder\n");
File::put($path.'/.env', "APP_KEY=titok\n");
deploymentGit($path, ['add', '-A', '-f']);
deploymentGit($path, ['commit', '-m', "második commit\n\nA törzsben említett árlista részlet."]);
deploymentGit($path, ['mv', 'app/Atnevezendo.php', 'app/Atnevezett.php']);
deploymentGit($path, ['rm', 'app/Regi.php']);
deploymentGit($path, ['commit', '-m', 'harmadik commit']);
$log = trim(deploymentGit($path, ['log', '--reverse', '--pretty=format:%H']));
// A mintából minden teszt másol, tehát tilos beleírni - a sample hookokat viszont
// felesleges körbemásolni, azokra a git nem támaszkodik.
deploymentDeleteDirectory($path.'/.git/hooks');
// A takarítás a PROCESSZ végén fut. Pest afterAll() nem jó rá: az fájlonként futna
// le, és a következő teszt-fájl alól húzná ki a mintát.
if ($elsoFelepites) {
register_shutdown_function(fn () => deploymentDeleteDirectory(deploymentTemplatePath()));
}
return $template = ['path' => $path, 'commits' => explode("\n", $log)];
}
/**
* A teszt saját, írható másolata a mintáról: a builder és a "working tree elrontása"
* tesztek beleírnak a repóba, ezért nem oszthatnak közös példányt.
*
* @return array{path:string, commits:array<int, string>}
*/
function deploymentTestRepository(): array
{
$template = deploymentRepositoryTemplate();
$path = sys_get_temp_dir().DIRECTORY_SEPARATOR.'deployment-package-test-'.uniqid();
File::copyDirectory($template['path'], $path);
Config::set('deployment.repo_path', $path);
Config::set('deployment.output_path', $path.DIRECTORY_SEPARATOR.'_kimenet');
return ['path' => $path, 'commits' => $template['commits']];
}
/**
* Facade nélküli törlés: a shutdown függvény a booteolt alkalmazáson kívül is fut.
*/
function deploymentDeleteDirectory(string $path): void
{
if (! is_dir($path)) {
return;
}
$items = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($items as $item) {
// A .git/objects tartalma Windowson csak olvasható, ezért törlés előtt fel kell oldani.
@chmod($item->getPathname(), 0777);
$item->isDir() ? @rmdir($item->getPathname()) : @unlink($item->getPathname());
}
@rmdir($path);
}
function deploymentCleanup(string $path): void
{
deploymentDeleteDirectory($path);
}