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>
182 lines
6.1 KiB
PHP
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);
|
|
}
|