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>
This commit is contained in:
parent
a9758e8da7
commit
98cf955354
92
tests/Feature/Deployment/DeploymentPackageAccessTest.php
Normal file
92
tests/Feature/Deployment/DeploymentPackageAccessTest.php
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Filament\Pages\DeploymentPackage;
|
||||||
|
use App\Models\FeatureFlag;
|
||||||
|
use App\Models\Role;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\Deployment\DeploymentGuard;
|
||||||
|
use App\Services\FeatureFlagRegistrar;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
require_once __DIR__.'/DeploymentTestHelpers.php';
|
||||||
|
|
||||||
|
uses(TestCase::class, RefreshDatabase::class);
|
||||||
|
|
||||||
|
// Az oldal betöltéskor lekérdezi a környezetek deploy-version.json-ját - a tesztek
|
||||||
|
// nem indíthatnak valódi kimenő kérést.
|
||||||
|
beforeEach(function () {
|
||||||
|
Http::fake();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a kikapcsolt env kapcsoló mindenkitől elveszi a hozzáférést', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
Config::set('deployment.enabled', false);
|
||||||
|
|
||||||
|
$developer = deploymentDeveloper();
|
||||||
|
|
||||||
|
expect(app(DeploymentGuard::class)->isAllowed($developer))->toBeFalse()
|
||||||
|
->and(app(DeploymentGuard::class)->denialReason($developer))->toContain('DEPLOYMENT_PACKAGE_ENABLED');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a nem fejlesztői stage-en akkor sem érhető el, ha a kapcsoló be van kapcsolva', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
Config::set('app.stage', 'PROD');
|
||||||
|
|
||||||
|
$developer = deploymentDeveloper();
|
||||||
|
|
||||||
|
expect(app(DeploymentGuard::class)->isAllowed($developer))->toBeFalse()
|
||||||
|
->and(app(DeploymentGuard::class)->denialReason($developer))->toContain('fejlesztői környezetben');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a developer szerep nélküli felhasználó nem éri el', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
|
||||||
|
Role::create(['name' => 'admin', 'display_name' => 'Admin']);
|
||||||
|
$admin = User::factory()->create();
|
||||||
|
$admin->addRole(Role::where('name', 'admin')->first());
|
||||||
|
|
||||||
|
FeatureFlag::create([
|
||||||
|
'name' => 'DeploymentPackage',
|
||||||
|
'label' => 'Deployment csomagoló',
|
||||||
|
'enabled' => true,
|
||||||
|
'stages' => null,
|
||||||
|
'roles' => null,
|
||||||
|
]);
|
||||||
|
app(FeatureFlagRegistrar::class)->registerAll();
|
||||||
|
|
||||||
|
expect(app(DeploymentGuard::class)->isAllowed($admin))->toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a kikapcsolt feature flag elrejti a felületet a fejlesztő elől is', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
|
||||||
|
$developer = deploymentDeveloper(flagEnabled: false);
|
||||||
|
|
||||||
|
expect(app(DeploymentGuard::class)->isAllowed($developer))->toBeFalse()
|
||||||
|
->and(DeploymentPackage::canAccess())->toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('minden feltétel teljesülése esetén engedélyezett', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
|
||||||
|
$developer = deploymentDeveloper();
|
||||||
|
|
||||||
|
expect(app(DeploymentGuard::class)->isAllowed($developer))->toBeTrue()
|
||||||
|
->and(app(DeploymentGuard::class)->denialReason($developer))->toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a jogosulatlan felhasználó 403-at kap az oldal betöltésekor', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
|
||||||
|
$developer = deploymentDeveloper(flagEnabled: false);
|
||||||
|
|
||||||
|
// Szándékosan valódi HTTP kérés: a Livewire::test() a HttpException-t válasszá
|
||||||
|
// alakítja (RequestBroker: withoutExceptionHandling([HttpException::class])), így
|
||||||
|
// ott nem a tényleges belépési pontot mérnénk.
|
||||||
|
$this->actingAs($developer)
|
||||||
|
->get(DeploymentPackage::getUrl())
|
||||||
|
->assertForbidden();
|
||||||
|
});
|
||||||
168
tests/Feature/Deployment/DeploymentPackageBuilderTest.php
Normal file
168
tests/Feature/Deployment/DeploymentPackageBuilderTest.php
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Services\Deployment\DeploymentPackageBuilder;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
require_once __DIR__.'/DeploymentTestHelpers.php';
|
||||||
|
|
||||||
|
uses(TestCase::class, RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
Http::fake();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a teendők kérik a queue worker és a scheduler újraindítását, a cache ürítés után', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$package = app(DeploymentPackageBuilder::class)->build('d2d', 'test', $first, $third);
|
||||||
|
$teendok = File::get($package['path'].'/_TEENDOK.md');
|
||||||
|
|
||||||
|
expect($teendok)->toContain('docker service update --force d2d-environment_queue')
|
||||||
|
->and($teendok)->toContain('docker service update --force d2d-environment_scheduler')
|
||||||
|
// A 0 replikás scheduler miatt tudni kell, hogy a restart üresjárat lehet.
|
||||||
|
->and($teendok)->toContain('0 replikán fut');
|
||||||
|
|
||||||
|
// A restartnak a cache ürítés UTÁN kell jönnie: a frissen induló worker különben
|
||||||
|
// a régi, cache-elt konfigot töltené be.
|
||||||
|
expect(strpos($teendok, 'docker service update'))->toBeGreaterThan(strpos($teendok, 'config:clear'));
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a queue worker nélküli környezetnél is kiderül, mit kell majd újraindítani', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
||||||
|
$teendok = File::get($package['path'].'/_TEENDOK.md');
|
||||||
|
|
||||||
|
expect($teendok)->toContain('docker service update --force t2t_scheduler')
|
||||||
|
->and($teendok)->toContain('t2t_queue');
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a teendők között szerepel a seeder futtatása a konkrét osztálynévvel', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
||||||
|
$teendok = File::get($package['path'].'/_TEENDOK.md');
|
||||||
|
|
||||||
|
expect($teendok)->toContain('php artisan db:seed --class=TesztSeeder')
|
||||||
|
// A seeder puszta felmásolása nem futtatja le - ezt ki kell mondani.
|
||||||
|
->and($teendok)->toContain('NEM futtatja le')
|
||||||
|
// A Pennant purge feltételes: Eloquent mentésnél a FeatureFlagObserver elvégzi.
|
||||||
|
->and($teendok)->toContain('pennant:purge')
|
||||||
|
->and($teendok)->toContain('cache:clear');
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a csomag a commitolt tartalmat viszi, nem a working tree állapotát', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
// A working tree szándékos elrontása a commit után: ez nem kerülhet a csomagba.
|
||||||
|
File::put($repository['path'].'/app/Árlista.php', "<?php\n// EZ NEM KERÜLHET A CSOMAGBA\n");
|
||||||
|
|
||||||
|
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
||||||
|
|
||||||
|
expect(File::get($package['path'].'/app/Árlista.php'))->not->toContain('NEM KERÜLHET')
|
||||||
|
// A .gitattributes eol=lf miatt a git archive LF-fel ír, Windowson is.
|
||||||
|
->and(File::get($package['path'].'/app/Árlista.php'))->not->toContain("\r\n");
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a csomagból kimarad a kizárt és a kézzel kivett fájl, a törlendők külön listára kerülnek', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third, ['app/Elso.php']);
|
||||||
|
|
||||||
|
expect(File::exists($package['path'].'/app/Atnevezett.php'))->toBeTrue()
|
||||||
|
->and(File::exists($package['path'].'/app/Elso.php'))->toBeFalse()
|
||||||
|
->and(File::exists($package['path'].'/.env'))->toBeFalse()
|
||||||
|
->and(File::exists($package['path'].'/app/Regi.php'))->toBeFalse()
|
||||||
|
->and($package['counts']['skipped'])->toBe(1)
|
||||||
|
->and($package['counts']['missing'])->toBe(0);
|
||||||
|
|
||||||
|
$torlendo = File::get($package['path'].'/_TORLENDO.txt');
|
||||||
|
|
||||||
|
expect($torlendo)->toContain('app/Regi.php')
|
||||||
|
->and($torlendo)->toContain('app/Atnevezendo.php');
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a csomag rögzíti a kirakott verziót', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
||||||
|
|
||||||
|
$version = json_decode(File::get($package['path'].'/public/deploy-version.json'), true);
|
||||||
|
|
||||||
|
expect($version['commit'])->toBe($third)
|
||||||
|
->and($version['target'])->toBe('e2e')
|
||||||
|
->and($version['branch'])->toBe('main');
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a limit fölötti fájlszámnál nem készül csomag', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
Config::set('deployment.max_files', 1);
|
||||||
|
|
||||||
|
expect(fn () => app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third))
|
||||||
|
->toThrow(RuntimeException::class);
|
||||||
|
|
||||||
|
expect(File::exists(config('deployment.output_path')))->toBeFalse();
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a csomagoló szerveren akkor sem indul el, ha közvetlenül hívják', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
Config::set('app.stage', 'PROD');
|
||||||
|
|
||||||
|
expect(fn () => app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third))
|
||||||
|
->toThrow(RuntimeException::class);
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a törlő szkript dry runban fut és ellenőrzi az app gyökeret', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
||||||
|
$script = File::get($package['path'].'/_torles.sh');
|
||||||
|
|
||||||
|
expect($script)->toStartWith('#!/usr/bin/env bash')
|
||||||
|
->and($script)->toContain('[[ -f artisan && -d app ]]')
|
||||||
|
->and($script)->toContain('"app/Regi.php"')
|
||||||
|
// Windowson generáljuk, Linuxon fut: CRLF-fel a shebang sor törne el.
|
||||||
|
->and($script)->not->toContain("\r\n");
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
70
tests/Feature/Deployment/DeploymentPackageCreationTest.php
Normal file
70
tests/Feature/Deployment/DeploymentPackageCreationTest.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Filament\Pages\DeploymentPackage;
|
||||||
|
use App\Models\DeploymentPackage as DeploymentPackageRecord;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
require_once __DIR__.'/DeploymentTestHelpers.php';
|
||||||
|
|
||||||
|
uses(TestCase::class, RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
Http::fake();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a felületről indított csomagolás létrehozza a mappát', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$this->actingAs(deploymentDeveloper());
|
||||||
|
|
||||||
|
$component = Livewire::test(DeploymentPackage::class)
|
||||||
|
->call('selectFrom', $first)
|
||||||
|
->call('selectTo', $third)
|
||||||
|
->call('toggleFile', 'app/Elso.php')
|
||||||
|
->call('createPackage');
|
||||||
|
|
||||||
|
$package = $component->get('lastPackage');
|
||||||
|
|
||||||
|
// Alapértelmezett célkörnyezet a d2d: azt frissítjük sűrűbben.
|
||||||
|
expect($package)->not->toBeNull()
|
||||||
|
->and($package['name'])->toStartWith('deploy_d2d_')
|
||||||
|
->and(File::isDirectory($package['path']))->toBeTrue()
|
||||||
|
->and($package['skipped'])->toBe(['app/Elso.php']);
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a kirakottként megjelölt csomag adja a következő tartomány kezdetét', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, $second] = $repository['commits'];
|
||||||
|
|
||||||
|
$this->actingAs(deploymentDeveloper());
|
||||||
|
|
||||||
|
// Az alapértelmezett célkörnyezetre készül, hogy a második oldalbetöltés is ezt töltse elő.
|
||||||
|
$record = DeploymentPackageRecord::create([
|
||||||
|
'name' => 'deploy_d2d_teszt',
|
||||||
|
'target' => 'd2d',
|
||||||
|
'branch' => 'main',
|
||||||
|
'from_commit' => $first,
|
||||||
|
'to_commit' => $second,
|
||||||
|
'folder' => $repository['path'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::test(DeploymentPackage::class)
|
||||||
|
->call('markDeployed', $record->id)
|
||||||
|
->assertSet('fromCommit', $second);
|
||||||
|
|
||||||
|
expect($record->refresh()->deployed_at)->not->toBeNull();
|
||||||
|
|
||||||
|
// Új oldalbetöltésnél is a kirakott commitról indul a tartomány.
|
||||||
|
Livewire::test(DeploymentPackage::class)->assertSet('fromCommit', $second);
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
70
tests/Feature/Deployment/DeploymentPackageDownloadTest.php
Normal file
70
tests/Feature/Deployment/DeploymentPackageDownloadTest.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Filament\Pages\DeploymentPackage;
|
||||||
|
use App\Models\DeploymentPackage as DeploymentPackageRecord;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
require_once __DIR__.'/DeploymentTestHelpers.php';
|
||||||
|
|
||||||
|
uses(TestCase::class, RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
Http::fake();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a csomagolás naplóbejegyzést és letölthető ZIP-et hagy maga után', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$this->actingAs(deploymentDeveloper());
|
||||||
|
|
||||||
|
Livewire::test(DeploymentPackage::class)
|
||||||
|
->call('selectFrom', $first)
|
||||||
|
->call('selectTo', $third)
|
||||||
|
->call('createPackage');
|
||||||
|
|
||||||
|
$record = DeploymentPackageRecord::query()->latest('id')->first();
|
||||||
|
|
||||||
|
expect($record)->not->toBeNull()
|
||||||
|
->and($record->target)->toBe('d2d')
|
||||||
|
->and($record->to_commit)->toBe($third)
|
||||||
|
->and($record->deployed_at)->toBeNull()
|
||||||
|
->and(File::exists($record->zip_path))->toBeTrue();
|
||||||
|
|
||||||
|
Livewire::test(DeploymentPackage::class)
|
||||||
|
->call('downloadPackage', $record->id)
|
||||||
|
->assertFileDownloaded();
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a kimeneti mappán kívüli ZIP útvonal nem tölthető le', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$this->actingAs(deploymentDeveloper());
|
||||||
|
|
||||||
|
// Elrontott (vagy szándékosan átírt) rekord: a fájl létezik, de a csomagoló
|
||||||
|
// kimeneti mappáján kívül van.
|
||||||
|
$record = DeploymentPackageRecord::create([
|
||||||
|
'name' => 'deploy_e2e_hamis',
|
||||||
|
'target' => 'e2e',
|
||||||
|
'branch' => 'main',
|
||||||
|
'from_commit' => $first,
|
||||||
|
'to_commit' => $third,
|
||||||
|
'folder' => $repository['path'],
|
||||||
|
'zip_path' => base_path('composer.json'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::test(DeploymentPackage::class)
|
||||||
|
->call('downloadPackage', $record->id)
|
||||||
|
->assertNoFileDownloaded();
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
95
tests/Feature/Deployment/DeploymentPackageGitTest.php
Normal file
95
tests/Feature/Deployment/DeploymentPackageGitTest.php
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Services\Deployment\ChangeSetAnalyzer;
|
||||||
|
use App\Services\Deployment\GitRepository;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
require_once __DIR__.'/DeploymentTestHelpers.php';
|
||||||
|
|
||||||
|
uses(TestCase::class, RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
Http::fake();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a commitlista a legfrissebbtől visszafelé, fájlszámmal együtt jön', function () {
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
|
||||||
|
$commits = app(GitRepository::class)->commits('main', 10);
|
||||||
|
|
||||||
|
expect($commits)->toHaveCount(3)
|
||||||
|
->and($commits[0]['subject'])->toBe('harmadik commit')
|
||||||
|
->and($commits[2]['subject'])->toBe('első commit')
|
||||||
|
->and($commits[0]['hash'])->toHaveLength(40)
|
||||||
|
->and($commits[0]['file_count'])->toBe(2)
|
||||||
|
// A törzs a tárgysor nélkül jön, és nem tapad hozzá a --shortstat sora sem.
|
||||||
|
->and($commits[1]['body'])->toBe('A törzsben említett árlista részlet.')
|
||||||
|
->and($commits[0]['body'])->toBe('');
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a diff felismeri az új, módosult, törölt és átnevezett fájlokat', function () {
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$files = collect(app(GitRepository::class)->changedFiles($first, $third))->keyBy('path');
|
||||||
|
|
||||||
|
expect($files->get('app/Elso.php')['status'])->toBe('M')
|
||||||
|
->and($files->get('app/Regi.php')['status'])->toBe('D')
|
||||||
|
->and($files->get('app/Atnevezett.php')['status'])->toBe('R')
|
||||||
|
->and($files->get('app/Atnevezett.php')['old_path'])->toBe('app/Atnevezendo.php')
|
||||||
|
// Ékezetes útvonal: core.quotePath=false nélkül "app/\303\201rlista.php" jönne vissza.
|
||||||
|
->and($files->has('app/Árlista.php'))->toBeTrue();
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('az elemzés kizárja a tiltott fájlokat és külön kezeli a törlendőket', function () {
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$changedFiles = app(GitRepository::class)->changedFiles($first, $third);
|
||||||
|
$changeSet = app(ChangeSetAnalyzer::class)->analyze($changedFiles);
|
||||||
|
|
||||||
|
expect($changeSet['copied'])->toContain('app/Árlista.php', 'app/Atnevezett.php', 'app/Elso.php')
|
||||||
|
->and($changeSet['copied'])->not->toContain('.env')
|
||||||
|
->and($changeSet['counts']['excluded'])->toBe(1)
|
||||||
|
// A törölt fájl mellett az átnevezés régi útvonala is törlendő a célgépen.
|
||||||
|
->and($changeSet['deleted'])->toContain('app/Regi.php', 'app/Atnevezendo.php');
|
||||||
|
|
||||||
|
$titles = collect($changeSet['warnings'])->pluck('title')->implode(' | ');
|
||||||
|
|
||||||
|
expect($titles)->toContain('Migráció')
|
||||||
|
->and($titles)->toContain('Seeder')
|
||||||
|
->and($titles)->toContain('Törlendő fájl');
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('az érvénytelen commit azonosítót nem adja tovább a gitnek', function () {
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
|
||||||
|
$git = app(GitRepository::class);
|
||||||
|
|
||||||
|
expect($git->resolveCommit('../../etc/passwd'))->toBeNull()
|
||||||
|
->and($git->resolveCommit('main; rm -rf /'))->toBeNull()
|
||||||
|
->and($git->resolveCommit('HEAD'))->toBeNull()
|
||||||
|
->and($git->resolveCommit($repository['commits'][0]))->toBe($repository['commits'][0]);
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a fordított tartomány felismerhető', function () {
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$git = app(GitRepository::class);
|
||||||
|
|
||||||
|
expect($git->isAncestor($first, $third))->toBeTrue()
|
||||||
|
->and($git->isAncestor($third, $first))->toBeFalse();
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
90
tests/Feature/Deployment/DeploymentPackagePageTest.php
Normal file
90
tests/Feature/Deployment/DeploymentPackagePageTest.php
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Filament\Pages\DeploymentPackage;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Http\Client\Factory;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
require_once __DIR__.'/DeploymentTestHelpers.php';
|
||||||
|
|
||||||
|
uses(TestCase::class, RefreshDatabase::class);
|
||||||
|
|
||||||
|
// Az oldal betöltéskor lekérdezi a környezetek deploy-version.json-ját - a tesztek
|
||||||
|
// nem indíthatnak valódi kimenő kérést.
|
||||||
|
beforeEach(function () {
|
||||||
|
Http::fake();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a keresés a commit törzsében is talál, nem csak a tárgysorban', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
|
||||||
|
$this->actingAs(deploymentDeveloper());
|
||||||
|
|
||||||
|
Livewire::test(DeploymentPackage::class)
|
||||||
|
->set('data.search', 'árlista részlet')
|
||||||
|
->assertSee('második commit')
|
||||||
|
->assertDontSee('harmadik commit');
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('az oldal kilistázza a commitokat és a kijelölt tartomány fájljait', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[$first, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
$this->actingAs(deploymentDeveloper());
|
||||||
|
|
||||||
|
Livewire::test(DeploymentPackage::class)
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertSee('harmadik commit')
|
||||||
|
->call('selectFrom', $first)
|
||||||
|
->call('selectTo', $third)
|
||||||
|
->assertSet('fromCommit', $first)
|
||||||
|
->assertSet('toCommit', $third)
|
||||||
|
->assertSee('app/Atnevezett.php')
|
||||||
|
->assertSee('app/Regi.php');
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('az oldal alapból a d2d környezetet ajánlja és betöltéskor lekérdezi az élő állapotot', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
[, , $third] = $repository['commits'];
|
||||||
|
|
||||||
|
// A beforeEach-ben regisztrált catch-all stub minden URL-re illeszkedik, és a
|
||||||
|
// Http factory az ELSŐ találatot adja vissza - ezért itt új factory kell, különben
|
||||||
|
// az üres törzsű alapértelmezett válasz nyerne a konkrét stub helyett.
|
||||||
|
Http::swap(new Factory);
|
||||||
|
Http::fake([
|
||||||
|
'*deploy-version.json' => Http::response(['target' => 'd2d', 'commit' => $third, 'branch' => 'main']),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->actingAs(deploymentDeveloper());
|
||||||
|
|
||||||
|
$component = Livewire::test(DeploymentPackage::class);
|
||||||
|
|
||||||
|
expect($component->get('data')['target'])->toBe('d2d')
|
||||||
|
// A lekérdezés gomb nélkül, magától megtörténik.
|
||||||
|
->and($component->get('liveVersions')['d2d']['ok'])->toBeTrue()
|
||||||
|
->and($component->get('liveVersions')['d2d']['data']['commit'])->toBe($third);
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('az oldal nem fogad el kamu commit azonosítót', function () {
|
||||||
|
deploymentAllowEnvironment();
|
||||||
|
$repository = deploymentTestRepository();
|
||||||
|
|
||||||
|
$this->actingAs(deploymentDeveloper());
|
||||||
|
|
||||||
|
Livewire::test(DeploymentPackage::class)
|
||||||
|
->call('selectFrom', '../../etc/passwd')
|
||||||
|
->assertSet('fromCommit', null);
|
||||||
|
|
||||||
|
deploymentCleanup($repository['path']);
|
||||||
|
});
|
||||||
181
tests/Feature/Deployment/DeploymentTestHelpers.php
Normal file
181
tests/Feature/Deployment/DeploymentTestHelpers.php
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
<?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);
|
||||||
|
}
|
||||||
@ -1,668 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use App\Filament\Pages\DeploymentPackage;
|
|
||||||
use App\Models\DeploymentPackage as DeploymentPackageRecord;
|
|
||||||
use App\Models\FeatureFlag;
|
|
||||||
use App\Models\Role;
|
|
||||||
use App\Models\User;
|
|
||||||
use App\Services\Deployment\ChangeSetAnalyzer;
|
|
||||||
use App\Services\Deployment\DeploymentGuard;
|
|
||||||
use App\Services\Deployment\DeploymentPackageBuilder;
|
|
||||||
use App\Services\Deployment\GitRepository;
|
|
||||||
use App\Services\FeatureFlagRegistrar;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
use Illuminate\Http\Client\Factory;
|
|
||||||
use Illuminate\Support\Facades\Config;
|
|
||||||
use Illuminate\Support\Facades\File;
|
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
use Livewire\Livewire;
|
|
||||||
use Symfony\Component\Process\Process;
|
|
||||||
use Tests\TestCase;
|
|
||||||
|
|
||||||
uses(TestCase::class, RefreshDatabase::class);
|
|
||||||
|
|
||||||
// Az oldal betöltéskor lekérdezi a környezetek deploy-version.json-ját - a tesztek
|
|
||||||
// nem indíthatnak valódi kimenő kérést.
|
|
||||||
beforeEach(function () {
|
|
||||||
Http::fake();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 futásonké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;
|
|
||||||
|
|
||||||
if ($template !== null) {
|
|
||||||
return $template;
|
|
||||||
}
|
|
||||||
|
|
||||||
$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');
|
|
||||||
|
|
||||||
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: az afterAll 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
afterAll(function () {
|
|
||||||
deploymentDeleteDirectory(deploymentTemplatePath());
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a kikapcsolt env kapcsoló mindenkitől elveszi a hozzáférést', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
Config::set('deployment.enabled', false);
|
|
||||||
|
|
||||||
$developer = deploymentDeveloper();
|
|
||||||
|
|
||||||
expect(app(DeploymentGuard::class)->isAllowed($developer))->toBeFalse()
|
|
||||||
->and(app(DeploymentGuard::class)->denialReason($developer))->toContain('DEPLOYMENT_PACKAGE_ENABLED');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a nem fejlesztői stage-en akkor sem érhető el, ha a kapcsoló be van kapcsolva', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
Config::set('app.stage', 'PROD');
|
|
||||||
|
|
||||||
$developer = deploymentDeveloper();
|
|
||||||
|
|
||||||
expect(app(DeploymentGuard::class)->isAllowed($developer))->toBeFalse()
|
|
||||||
->and(app(DeploymentGuard::class)->denialReason($developer))->toContain('fejlesztői környezetben');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a developer szerep nélküli felhasználó nem éri el', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
|
|
||||||
Role::create(['name' => 'admin', 'display_name' => 'Admin']);
|
|
||||||
$admin = User::factory()->create();
|
|
||||||
$admin->addRole(Role::where('name', 'admin')->first());
|
|
||||||
|
|
||||||
FeatureFlag::create([
|
|
||||||
'name' => 'DeploymentPackage',
|
|
||||||
'label' => 'Deployment csomagoló',
|
|
||||||
'enabled' => true,
|
|
||||||
'stages' => null,
|
|
||||||
'roles' => null,
|
|
||||||
]);
|
|
||||||
app(FeatureFlagRegistrar::class)->registerAll();
|
|
||||||
|
|
||||||
expect(app(DeploymentGuard::class)->isAllowed($admin))->toBeFalse();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a kikapcsolt feature flag elrejti a felületet a fejlesztő elől is', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
|
|
||||||
$developer = deploymentDeveloper(flagEnabled: false);
|
|
||||||
|
|
||||||
expect(app(DeploymentGuard::class)->isAllowed($developer))->toBeFalse()
|
|
||||||
->and(DeploymentPackage::canAccess())->toBeFalse();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('minden feltétel teljesülése esetén engedélyezett', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
|
|
||||||
$developer = deploymentDeveloper();
|
|
||||||
|
|
||||||
expect(app(DeploymentGuard::class)->isAllowed($developer))->toBeTrue()
|
|
||||||
->and(app(DeploymentGuard::class)->denialReason($developer))->toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a jogosulatlan felhasználó 403-at kap az oldal betöltésekor', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
|
|
||||||
$developer = deploymentDeveloper(flagEnabled: false);
|
|
||||||
|
|
||||||
// Szándékosan valódi HTTP kérés: a Livewire::test() a HttpException-t válasszá
|
|
||||||
// alakítja (RequestBroker: withoutExceptionHandling([HttpException::class])), így
|
|
||||||
// ott nem a tényleges belépési pontot mérnénk.
|
|
||||||
$this->actingAs($developer)
|
|
||||||
->get(DeploymentPackage::getUrl())
|
|
||||||
->assertForbidden();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a commitlista a legfrissebbtől visszafelé, fájlszámmal együtt jön', function () {
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
|
|
||||||
$commits = app(GitRepository::class)->commits('main', 10);
|
|
||||||
|
|
||||||
expect($commits)->toHaveCount(3)
|
|
||||||
->and($commits[0]['subject'])->toBe('harmadik commit')
|
|
||||||
->and($commits[2]['subject'])->toBe('első commit')
|
|
||||||
->and($commits[0]['hash'])->toHaveLength(40)
|
|
||||||
->and($commits[0]['file_count'])->toBe(2)
|
|
||||||
// A törzs a tárgysor nélkül jön, és nem tapad hozzá a --shortstat sora sem.
|
|
||||||
->and($commits[1]['body'])->toBe('A törzsben említett árlista részlet.')
|
|
||||||
->and($commits[0]['body'])->toBe('');
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a keresés a commit törzsében is talál, nem csak a tárgysorban', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
|
|
||||||
$this->actingAs(deploymentDeveloper());
|
|
||||||
|
|
||||||
Livewire::test(DeploymentPackage::class)
|
|
||||||
->set('data.search', 'árlista részlet')
|
|
||||||
->assertSee('második commit')
|
|
||||||
->assertDontSee('harmadik commit');
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a diff felismeri az új, módosult, törölt és átnevezett fájlokat', function () {
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$files = collect(app(GitRepository::class)->changedFiles($first, $third))->keyBy('path');
|
|
||||||
|
|
||||||
expect($files->get('app/Elso.php')['status'])->toBe('M')
|
|
||||||
->and($files->get('app/Regi.php')['status'])->toBe('D')
|
|
||||||
->and($files->get('app/Atnevezett.php')['status'])->toBe('R')
|
|
||||||
->and($files->get('app/Atnevezett.php')['old_path'])->toBe('app/Atnevezendo.php')
|
|
||||||
// Ékezetes útvonal: core.quotePath=false nélkül "app/\303\201rlista.php" jönne vissza.
|
|
||||||
->and($files->has('app/Árlista.php'))->toBeTrue();
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('az elemzés kizárja a tiltott fájlokat és külön kezeli a törlendőket', function () {
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$changedFiles = app(GitRepository::class)->changedFiles($first, $third);
|
|
||||||
$changeSet = app(ChangeSetAnalyzer::class)->analyze($changedFiles);
|
|
||||||
|
|
||||||
expect($changeSet['copied'])->toContain('app/Árlista.php', 'app/Atnevezett.php', 'app/Elso.php')
|
|
||||||
->and($changeSet['copied'])->not->toContain('.env')
|
|
||||||
->and($changeSet['counts']['excluded'])->toBe(1)
|
|
||||||
// A törölt fájl mellett az átnevezés régi útvonala is törlendő a célgépen.
|
|
||||||
->and($changeSet['deleted'])->toContain('app/Regi.php', 'app/Atnevezendo.php');
|
|
||||||
|
|
||||||
$titles = collect($changeSet['warnings'])->pluck('title')->implode(' | ');
|
|
||||||
|
|
||||||
expect($titles)->toContain('Migráció')
|
|
||||||
->and($titles)->toContain('Seeder')
|
|
||||||
->and($titles)->toContain('Törlendő fájl');
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a teendők kérik a queue worker és a scheduler újraindítását, a cache ürítés után', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$package = app(DeploymentPackageBuilder::class)->build('d2d', 'test', $first, $third);
|
|
||||||
$teendok = File::get($package['path'].'/_TEENDOK.md');
|
|
||||||
|
|
||||||
expect($teendok)->toContain('docker service update --force d2d-environment_queue')
|
|
||||||
->and($teendok)->toContain('docker service update --force d2d-environment_scheduler')
|
|
||||||
// A 0 replikás scheduler miatt tudni kell, hogy a restart üresjárat lehet.
|
|
||||||
->and($teendok)->toContain('0 replikán fut');
|
|
||||||
|
|
||||||
// A restartnak a cache ürítés UTÁN kell jönnie: a frissen induló worker különben
|
|
||||||
// a régi, cache-elt konfigot töltené be.
|
|
||||||
expect(strpos($teendok, 'docker service update'))->toBeGreaterThan(strpos($teendok, 'config:clear'));
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a queue worker nélküli környezetnél is kiderül, mit kell majd újraindítani', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
|
||||||
$teendok = File::get($package['path'].'/_TEENDOK.md');
|
|
||||||
|
|
||||||
expect($teendok)->toContain('docker service update --force t2t_scheduler')
|
|
||||||
->and($teendok)->toContain('t2t_queue');
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a teendők között szerepel a seeder futtatása a konkrét osztálynévvel', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
|
||||||
$teendok = File::get($package['path'].'/_TEENDOK.md');
|
|
||||||
|
|
||||||
expect($teendok)->toContain('php artisan db:seed --class=TesztSeeder')
|
|
||||||
// A seeder puszta felmásolása nem futtatja le - ezt ki kell mondani.
|
|
||||||
->and($teendok)->toContain('NEM futtatja le')
|
|
||||||
// A Pennant purge feltételes: Eloquent mentésnél a FeatureFlagObserver elvégzi.
|
|
||||||
->and($teendok)->toContain('pennant:purge')
|
|
||||||
->and($teendok)->toContain('cache:clear');
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('az érvénytelen commit azonosítót nem adja tovább a gitnek', function () {
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
|
|
||||||
$git = app(GitRepository::class);
|
|
||||||
|
|
||||||
expect($git->resolveCommit('../../etc/passwd'))->toBeNull()
|
|
||||||
->and($git->resolveCommit('main; rm -rf /'))->toBeNull()
|
|
||||||
->and($git->resolveCommit('HEAD'))->toBeNull()
|
|
||||||
->and($git->resolveCommit($repository['commits'][0]))->toBe($repository['commits'][0]);
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a fordított tartomány felismerhető', function () {
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$git = app(GitRepository::class);
|
|
||||||
|
|
||||||
expect($git->isAncestor($first, $third))->toBeTrue()
|
|
||||||
->and($git->isAncestor($third, $first))->toBeFalse();
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('az oldal kilistázza a commitokat és a kijelölt tartomány fájljait', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$this->actingAs(deploymentDeveloper());
|
|
||||||
|
|
||||||
Livewire::test(DeploymentPackage::class)
|
|
||||||
->assertSuccessful()
|
|
||||||
->assertSee('harmadik commit')
|
|
||||||
->call('selectFrom', $first)
|
|
||||||
->call('selectTo', $third)
|
|
||||||
->assertSet('fromCommit', $first)
|
|
||||||
->assertSet('toCommit', $third)
|
|
||||||
->assertSee('app/Atnevezett.php')
|
|
||||||
->assertSee('app/Regi.php');
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a csomag a commitolt tartalmat viszi, nem a working tree állapotát', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
// A working tree szándékos elrontása a commit után: ez nem kerülhet a csomagba.
|
|
||||||
File::put($repository['path'].'/app/Árlista.php', "<?php\n// EZ NEM KERÜLHET A CSOMAGBA\n");
|
|
||||||
|
|
||||||
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
|
||||||
|
|
||||||
expect(File::get($package['path'].'/app/Árlista.php'))->not->toContain('NEM KERÜLHET')
|
|
||||||
// A .gitattributes eol=lf miatt a git archive LF-fel ír, Windowson is.
|
|
||||||
->and(File::get($package['path'].'/app/Árlista.php'))->not->toContain("\r\n");
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a csomagból kimarad a kizárt és a kézzel kivett fájl, a törlendők külön listára kerülnek', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third, ['app/Elso.php']);
|
|
||||||
|
|
||||||
expect(File::exists($package['path'].'/app/Atnevezett.php'))->toBeTrue()
|
|
||||||
->and(File::exists($package['path'].'/app/Elso.php'))->toBeFalse()
|
|
||||||
->and(File::exists($package['path'].'/.env'))->toBeFalse()
|
|
||||||
->and(File::exists($package['path'].'/app/Regi.php'))->toBeFalse()
|
|
||||||
->and($package['counts']['skipped'])->toBe(1)
|
|
||||||
->and($package['counts']['missing'])->toBe(0);
|
|
||||||
|
|
||||||
$torlendo = File::get($package['path'].'/_TORLENDO.txt');
|
|
||||||
|
|
||||||
expect($torlendo)->toContain('app/Regi.php')
|
|
||||||
->and($torlendo)->toContain('app/Atnevezendo.php');
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a csomag rögzíti a kirakott verziót', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
|
||||||
|
|
||||||
$version = json_decode(File::get($package['path'].'/public/deploy-version.json'), true);
|
|
||||||
|
|
||||||
expect($version['commit'])->toBe($third)
|
|
||||||
->and($version['target'])->toBe('e2e')
|
|
||||||
->and($version['branch'])->toBe('main');
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a limit fölötti fájlszámnál nem készül csomag', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
Config::set('deployment.max_files', 1);
|
|
||||||
|
|
||||||
expect(fn () => app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third))
|
|
||||||
->toThrow(RuntimeException::class);
|
|
||||||
|
|
||||||
expect(File::exists(config('deployment.output_path')))->toBeFalse();
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a csomagoló szerveren akkor sem indul el, ha közvetlenül hívják', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
Config::set('app.stage', 'PROD');
|
|
||||||
|
|
||||||
expect(fn () => app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third))
|
|
||||||
->toThrow(RuntimeException::class);
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a felületről indított csomagolás létrehozza a mappát', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$this->actingAs(deploymentDeveloper());
|
|
||||||
|
|
||||||
$component = Livewire::test(DeploymentPackage::class)
|
|
||||||
->call('selectFrom', $first)
|
|
||||||
->call('selectTo', $third)
|
|
||||||
->call('toggleFile', 'app/Elso.php')
|
|
||||||
->call('createPackage');
|
|
||||||
|
|
||||||
$package = $component->get('lastPackage');
|
|
||||||
|
|
||||||
// Alapértelmezett célkörnyezet a d2d: azt frissítjük sűrűbben.
|
|
||||||
expect($package)->not->toBeNull()
|
|
||||||
->and($package['name'])->toStartWith('deploy_d2d_')
|
|
||||||
->and(File::isDirectory($package['path']))->toBeTrue()
|
|
||||||
->and($package['skipped'])->toBe(['app/Elso.php']);
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a csomagolás naplóbejegyzést és letölthető ZIP-et hagy maga után', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$this->actingAs(deploymentDeveloper());
|
|
||||||
|
|
||||||
Livewire::test(DeploymentPackage::class)
|
|
||||||
->call('selectFrom', $first)
|
|
||||||
->call('selectTo', $third)
|
|
||||||
->call('createPackage');
|
|
||||||
|
|
||||||
$record = DeploymentPackageRecord::query()->latest('id')->first();
|
|
||||||
|
|
||||||
expect($record)->not->toBeNull()
|
|
||||||
->and($record->target)->toBe('d2d')
|
|
||||||
->and($record->to_commit)->toBe($third)
|
|
||||||
->and($record->deployed_at)->toBeNull()
|
|
||||||
->and(File::exists($record->zip_path))->toBeTrue();
|
|
||||||
|
|
||||||
Livewire::test(DeploymentPackage::class)
|
|
||||||
->call('downloadPackage', $record->id)
|
|
||||||
->assertFileDownloaded();
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a kirakottként megjelölt csomag adja a következő tartomány kezdetét', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, $second, $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$this->actingAs(deploymentDeveloper());
|
|
||||||
|
|
||||||
// Az alapértelmezett célkörnyezetre készül, hogy a második oldalbetöltés is ezt töltse elő.
|
|
||||||
$record = DeploymentPackageRecord::create([
|
|
||||||
'name' => 'deploy_d2d_teszt',
|
|
||||||
'target' => 'd2d',
|
|
||||||
'branch' => 'main',
|
|
||||||
'from_commit' => $first,
|
|
||||||
'to_commit' => $second,
|
|
||||||
'folder' => $repository['path'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Livewire::test(DeploymentPackage::class)
|
|
||||||
->call('markDeployed', $record->id)
|
|
||||||
->assertSet('fromCommit', $second);
|
|
||||||
|
|
||||||
expect($record->refresh()->deployed_at)->not->toBeNull();
|
|
||||||
|
|
||||||
// Új oldalbetöltésnél is a kirakott commitról indul a tartomány.
|
|
||||||
Livewire::test(DeploymentPackage::class)->assertSet('fromCommit', $second);
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a kimeneti mappán kívüli ZIP útvonal nem tölthető le', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$this->actingAs(deploymentDeveloper());
|
|
||||||
|
|
||||||
// Elrontott (vagy szándékosan átírt) rekord: a fájl létezik, de a csomagoló
|
|
||||||
// kimeneti mappáján kívül van.
|
|
||||||
$record = DeploymentPackageRecord::create([
|
|
||||||
'name' => 'deploy_e2e_hamis',
|
|
||||||
'target' => 'e2e',
|
|
||||||
'branch' => 'main',
|
|
||||||
'from_commit' => $first,
|
|
||||||
'to_commit' => $third,
|
|
||||||
'folder' => $repository['path'],
|
|
||||||
'zip_path' => base_path('composer.json'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
Livewire::test(DeploymentPackage::class)
|
|
||||||
->call('downloadPackage', $record->id)
|
|
||||||
->assertNoFileDownloaded();
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('a törlő szkript dry runban fut és ellenőrzi az app gyökeret', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[$first, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
$package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third);
|
|
||||||
$script = File::get($package['path'].'/_torles.sh');
|
|
||||||
|
|
||||||
expect($script)->toStartWith('#!/usr/bin/env bash')
|
|
||||||
->and($script)->toContain('[[ -f artisan && -d app ]]')
|
|
||||||
->and($script)->toContain('"app/Regi.php"')
|
|
||||||
// Windowson generáljuk, Linuxon fut: CRLF-fel a shebang sor törne el.
|
|
||||||
->and($script)->not->toContain("\r\n");
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('az oldal alapból a d2d környezetet ajánlja és betöltéskor lekérdezi az élő állapotot', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
[, , $third] = $repository['commits'];
|
|
||||||
|
|
||||||
// A beforeEach-ben regisztrált catch-all stub minden URL-re illeszkedik, és a
|
|
||||||
// Http factory az ELSŐ találatot adja vissza - ezért itt új factory kell, különben
|
|
||||||
// az üres törzsű alapértelmezett válasz nyerne a konkrét stub helyett.
|
|
||||||
Http::swap(new Factory);
|
|
||||||
Http::fake([
|
|
||||||
'*deploy-version.json' => Http::response(['target' => 'd2d', 'commit' => $third, 'branch' => 'main']),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->actingAs(deploymentDeveloper());
|
|
||||||
|
|
||||||
$component = Livewire::test(DeploymentPackage::class);
|
|
||||||
|
|
||||||
expect($component->get('data')['target'])->toBe('d2d')
|
|
||||||
// A lekérdezés gomb nélkül, magától megtörténik.
|
|
||||||
->and($component->get('liveVersions')['d2d']['ok'])->toBeTrue()
|
|
||||||
->and($component->get('liveVersions')['d2d']['data']['commit'])->toBe($third);
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('az oldal nem fogad el kamu commit azonosítót', function () {
|
|
||||||
deploymentAllowEnvironment();
|
|
||||||
$repository = deploymentTestRepository();
|
|
||||||
|
|
||||||
$this->actingAs(deploymentDeveloper());
|
|
||||||
|
|
||||||
Livewire::test(DeploymentPackage::class)
|
|
||||||
->call('selectFrom', '../../etc/passwd')
|
|
||||||
->assertSet('fromCommit', null);
|
|
||||||
|
|
||||||
deploymentCleanup($repository['path']);
|
|
||||||
});
|
|
||||||
Loading…
Reference in New Issue
Block a user