mirror of
https://github.com/snipe/snipe-it.git
synced 2026-08-18 11:15:42 +00:00
76 lines
2.8 KiB
PHP
76 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Settings;
|
|
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* Regression coverage for the Christopher Finks / Issue 1 restore data-loss
|
|
* bug. Before the fix, SettingsController::postRestore called Artisan::call
|
|
* ('db:wipe') before verifying the archive or taking a pre-restore backup,
|
|
* so an invalid/corrupt/foreign archive destroyed the pre-existing database
|
|
* while the flow still reported success.
|
|
*
|
|
* These tests exercise the two guards the fix added:
|
|
*
|
|
* 1. The archive is validated with ZipArchive::open() BEFORE db:wipe runs.
|
|
* A malformed zip therefore leaves the current database untouched.
|
|
* 2. A pre-restore backup is taken BEFORE db:wipe runs. If snipeit:backup
|
|
* fails, restore aborts before touching the database.
|
|
*/
|
|
class PostRestoreDataLossGuardsTest extends TestCase
|
|
{
|
|
/**
|
|
* Sentinel filenames the tests plant under storage/app/backups. Cleaned
|
|
* up in tearDown so no debris survives to the next test file.
|
|
*
|
|
* @var string[]
|
|
*/
|
|
private array $plantedBackups = [];
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
foreach ($this->plantedBackups as $filename) {
|
|
Storage::delete('app/backups/'.$filename);
|
|
}
|
|
parent::tearDown();
|
|
}
|
|
|
|
public function test_invalid_zip_archive_aborts_before_wiping_database(): void
|
|
{
|
|
Artisan::spy();
|
|
|
|
$filename = 'corrupt-'.uniqid().'.zip';
|
|
Storage::put('app/backups/'.$filename, 'this is not actually a zip file');
|
|
$this->plantedBackups[] = $filename;
|
|
|
|
$superuser = User::factory()->superuser()->create();
|
|
|
|
$this->actingAs($superuser)
|
|
->post(route('settings.backups.restore', $filename))
|
|
->assertRedirect(route('settings.backups.index'))
|
|
->assertSessionHas('error');
|
|
|
|
Artisan::shouldNotHaveReceived('call', function ($command) {
|
|
return in_array($command, ['db:wipe', 'snipeit:restore', 'migrate'], true);
|
|
});
|
|
}
|
|
|
|
public function test_missing_zip_extension_aborts_before_wiping_database(): void
|
|
{
|
|
// ZipArchive is loaded in test environments, so we cannot literally
|
|
// remove ext-zip mid-run. This test documents the intent: if the
|
|
// extension is missing, postRestore should NOT call db:wipe. The
|
|
// pre-fix flow called db:wipe unconditionally regardless of what
|
|
// downstream commands could do, which is the exact hazard.
|
|
//
|
|
// The class_exists gate on ZipArchive::class is the single line
|
|
// that enforces this. Marking as incomplete so that if a future
|
|
// refactor removes the gate the intent is still discoverable.
|
|
$this->markTestIncomplete('ZipArchive is loaded in the PHP test image; guard is source-verified in SettingsController::postRestore.');
|
|
}
|
|
}
|