116 lines
3.6 KiB
PHP
116 lines
3.6 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace Tests\Feature\Admin;
|
||
|
|
|
||
|
|
use App\Models\User;
|
||
|
|
use App\Services\BackupService;
|
||
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||
|
|
use Illuminate\Support\Facades\File;
|
||
|
|
use Illuminate\Support\Facades\Storage;
|
||
|
|
use Illuminate\Support\Facades\Cache;
|
||
|
|
use Tests\TestCase;
|
||
|
|
use App\Console\Commands\SiteRestore;
|
||
|
|
|
||
|
|
class BackupRestoreReliabilityTest extends TestCase
|
||
|
|
{
|
||
|
|
use RefreshDatabase;
|
||
|
|
|
||
|
|
protected $user;
|
||
|
|
protected $backupDir;
|
||
|
|
|
||
|
|
protected function setUp(): void
|
||
|
|
{
|
||
|
|
parent::setUp();
|
||
|
|
$this->user = User::factory()->create();
|
||
|
|
|
||
|
|
$role = \App\Models\Role::create(['name' => 'Admin', 'slug' => 'admin', 'is_protected' => true]);
|
||
|
|
$this->user->roles()->attach($role);
|
||
|
|
|
||
|
|
$this->backupDir = storage_path('app/backups');
|
||
|
|
if (!File::exists($this->backupDir)) {
|
||
|
|
File::makeDirectory($this->backupDir, 0755, true);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function tearDown(): void
|
||
|
|
{
|
||
|
|
if (File::exists($this->backupDir)) {
|
||
|
|
File::cleanDirectory($this->backupDir);
|
||
|
|
}
|
||
|
|
parent::tearDown();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Test that a pre-restore snapshot is automatically created before restoration.
|
||
|
|
*/
|
||
|
|
public function test_pre_restore_snapshot_is_created()
|
||
|
|
{
|
||
|
|
// 1. Create a dummy backup to restore from
|
||
|
|
$this->actingAs($this->user)->post(route('admin.backups.store'));
|
||
|
|
$backups = File::files($this->backupDir);
|
||
|
|
$this->assertCount(1, $backups);
|
||
|
|
$restoreFilename = $backups[0]->getFilename();
|
||
|
|
|
||
|
|
// Wait a second to ensure a different timestamp for the snapshot
|
||
|
|
sleep(1);
|
||
|
|
|
||
|
|
// 2. Clear progress cache
|
||
|
|
Cache::forget(SiteRestore::PROGRESS_KEY);
|
||
|
|
|
||
|
|
// 3. Trigger restoration
|
||
|
|
$response = $this->actingAs($this->user)->post(route('admin.backups.restore'), [
|
||
|
|
'filename' => $restoreFilename,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response->assertSessionHas('success');
|
||
|
|
|
||
|
|
// 4. Verify that we now have 2 backups (the original + the pre-restore snapshot)
|
||
|
|
$backupsAfter = File::files($this->backupDir);
|
||
|
|
$this->assertCount(2, $backupsAfter);
|
||
|
|
|
||
|
|
// One of them should be the original, and one should be the snapshot
|
||
|
|
// (though they currently have similar naming, BackupService handles finding the newest)
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Test that restoration progress is tracked in Cache.
|
||
|
|
*/
|
||
|
|
public function test_restoration_progress_is_tracked()
|
||
|
|
{
|
||
|
|
// 1. Create a dummy backup
|
||
|
|
$this->actingAs($this->user)->post(route('admin.backups.store'));
|
||
|
|
$restoreFilename = File::files($this->backupDir)[0]->getFilename();
|
||
|
|
|
||
|
|
// 2. Trigger restoration
|
||
|
|
$this->actingAs($this->user)->post(route('admin.backups.restore'), [
|
||
|
|
'filename' => $restoreFilename,
|
||
|
|
]);
|
||
|
|
|
||
|
|
// 3. Verify progress was recorded (at the end it should be 100%)
|
||
|
|
$progress = Cache::get(SiteRestore::PROGRESS_KEY);
|
||
|
|
$this->assertNotNull($progress);
|
||
|
|
$this->assertEquals(100, $progress['percent']);
|
||
|
|
$this->assertEquals('Restoration completed successfully.', $progress['status']);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Test the progress polling endpoint.
|
||
|
|
*/
|
||
|
|
public function test_progress_polling_endpoint()
|
||
|
|
{
|
||
|
|
Cache::put(SiteRestore::PROGRESS_KEY, [
|
||
|
|
'status' => 'Testing Progress',
|
||
|
|
'percent' => 50,
|
||
|
|
'timestamp' => now()->timestamp,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->actingAs($this->user)->get(route('admin.backups.restore.progress'));
|
||
|
|
|
||
|
|
$response->assertStatus(200);
|
||
|
|
$response->assertJson([
|
||
|
|
'status' => 'Testing Progress',
|
||
|
|
'percent' => 50,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
}
|