A csomagolás mostantól nyomot hagy: melyik környezetre mi készült, mi lett kirakva, és mennyi a lemaradás - ez az, ami a "melyik verzió fut az e2e-n" kérdést kiveszi a fejből. - deployment_packages tábla + modell (BaseAuditable): target, tartomány, darabszámok, mappa/ZIP útvonal, deployed_at. A csomagolás és a tényleges kirakás két külön esemény. - Targetenkénti előtöltés: a kezdő commit az adott környezetre utoljára kirakottként megjelölt csomag to_commit-je. Rebase után nem létező hash esetén inkább üres marad, mint hogy hamis tartományt mutasson. - "Környezetek állapota" panel: napló szerinti állapot + commit-lemaradás (git rev-list --count), és gombra a szerver deploy-version.json-jának lekérdezése. Ha a kettő eltér, az elmaradt vagy félbemaradt feltöltés jele. Szándékosan gombra fut, nem minden rendereléskor: így nem indul kimenő kérés magától. - ZIP a kész mappából (a fájllista a zip létrejötte előtt készül, így nem csomagolja magát), letöltés a naplóból. A DB-ből jövő útvonalat kiírás előtt a kimeneti mappához kötjük, hogy egy módosított rekord se tehessen letölthetővé tetszőleges fájlt. - _torles.sh: alapból dry run, --confirm kell a törléshez, app-gyökér ellenőrzéssel, abszolút útvonal és .. kiszűrésével, LF sorvéggel és BOM nélkül. Lefuttatva ellenőrizve. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
515 lines
18 KiB
PHP
515 lines
18 KiB
PHP
<?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\Support\Facades\Config;
|
|
use Illuminate\Support\Facades\File;
|
|
use Livewire\Livewire;
|
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
|
use Symfony\Component\Process\Process;
|
|
use Tests\TestCase;
|
|
|
|
uses(TestCase::class, RefreshDatabase::class);
|
|
|
|
/**
|
|
* 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();
|
|
}
|
|
|
|
/**
|
|
* Determinisztikus teszt-repó: átnevezés, törlés, ékezetes fájlnév és migráció is van benne.
|
|
*
|
|
* @return array{path:string, commits:array<int, string>}
|
|
*/
|
|
function deploymentTestRepository(): array
|
|
{
|
|
$path = sys_get_temp_dir().DIRECTORY_SEPARATOR.'deployment-package-test-'.uniqid();
|
|
|
|
File::makeDirectory($path.'/app', 0777, true);
|
|
File::makeDirectory($path.'/database/migrations', 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.'/.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']));
|
|
|
|
Config::set('deployment.repo_path', $path);
|
|
Config::set('deployment.output_path', $path.DIRECTORY_SEPARATOR.'_kimenet');
|
|
|
|
return ['path' => $path, 'commits' => explode("\n", $log)];
|
|
}
|
|
|
|
function deploymentCleanup(string $path): void
|
|
{
|
|
// A .git/objects tartalma Windowson csak olvasható, ezért törlés előtt fel kell oldani.
|
|
foreach (File::allFiles($path, true) as $file) {
|
|
@chmod($file->getPathname(), 0777);
|
|
}
|
|
|
|
try {
|
|
File::deleteDirectory($path);
|
|
} catch (Throwable) {
|
|
// A takarítás nem futtathat el tesztet - a temp mappa maradványa nem befolyásol semmit.
|
|
}
|
|
}
|
|
|
|
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);
|
|
$this->actingAs($developer);
|
|
|
|
expect(fn () => Livewire::test(DeploymentPackage::class))->toThrow(HttpException::class);
|
|
});
|
|
|
|
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('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']);
|
|
});
|
|
|
|
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');
|
|
|
|
expect($package)->not->toBeNull()
|
|
->and($package['name'])->toStartWith('deploy_e2e_')
|
|
->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('e2e')
|
|
->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());
|
|
|
|
$record = DeploymentPackageRecord::create([
|
|
'name' => 'deploy_e2e_teszt',
|
|
'target' => 'e2e',
|
|
'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 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']);
|
|
});
|