cms/tests/Feature/Admin/ReliabilityTest.php

211 lines
6.5 KiB
PHP
Raw Permalink Normal View History

<?php
namespace Tests\Feature\Admin;
use App\Models\User;
use App\Models\Role;
use App\Models\Permission;
use App\Models\Media;
use App\Models\Page;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\File;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
class ReliabilityTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->seed(\Database\Seeders\PermissionSeeder::class);
$this->seed(\Database\Seeders\RoleSeeder::class);
$adminRole = Role::where('slug', 'admin')->first();
$this->user = User::factory()->create();
$this->user->roles()->attach($adminRole);
}
#[Test]
public function admin_can_create_backup_via_cli()
{
// Setup some themes and media
$themesPath = base_path('themes/test-backup-theme');
if (!File::isDirectory($themesPath)) {
File::makeDirectory($themesPath, 0755, true);
}
File::put($themesPath . '/theme.md', 'title: Test');
Storage::disk('public')->put('media/test.jpg', 'content');
$exitCode = Artisan::call('sw:site:backup');
$this->assertEquals(0, $exitCode);
$backupDir = storage_path('app/backups');
$this->assertTrue(File::isDirectory($backupDir));
$files = File::files($backupDir);
$this->assertNotEmpty($files);
// Cleanup
File::deleteDirectory($themesPath);
File::deleteDirectory($backupDir);
}
#[Test]
public function admin_can_trigger_backup_via_web()
{
$response = $this->actingAs($this->user)
->post(route('admin.backups.store'));
$response->assertStatus(302);
$response->assertSessionHas('success');
$backupDir = storage_path('app/backups');
$this->assertTrue(File::isDirectory($backupDir));
// Cleanup
File::deleteDirectory($backupDir);
}
#[Test]
public function admin_can_restore_via_web()
{
// 1. Create a backup first
$this->actingAs($this->user)->post(route('admin.backups.store'));
$backupDir = storage_path('app/backups');
$files = File::files($backupDir);
$this->assertNotEmpty($files);
$filename = $files[0]->getFilename();
// 2. Mocking restoration for tests because it overwrites DB/Files
// In this environment, we just want to ensure the controller calls the command.
// But let's try a real call to see if it works with our SiteRestore logic.
$response = $this->actingAs($this->user)
->post(route('admin.backups.restore'), [
'filename' => $filename,
]);
$response->assertStatus(302);
$response->assertSessionHas('success');
// Cleanup
File::deleteDirectory($backupDir);
}
#[Test]
public function admin_can_download_backup()
{
$this->actingAs($this->user);
Artisan::call('sw:site:backup');
$backupDir = storage_path('app/backups');
$files = File::files($backupDir);
$filename = $files[0]->getFilename();
$response = $this->get(route('admin.backups.download', ['filename' => $filename]));
$response->assertOk();
$response->assertHeader('Content-Disposition', 'attachment; filename=' . $filename);
// Cleanup
File::deleteDirectory($backupDir);
}
#[Test]
public function admin_can_upload_backup()
{
$backupDir = storage_path('app/backups');
if (File::exists($backupDir)) {
File::deleteDirectory($backupDir);
}
$filename = 'test_upload.gz';
$file = \Illuminate\Http\Testing\File::create($filename, 10); // 10KB dummy gz
$response = $this->actingAs($this->user)
->post(route('admin.backups.upload'), [
'backup_file' => $file,
]);
$response->assertStatus(302);
$response->assertSessionHas('success');
$this->assertTrue(File::exists($backupDir . '/' . $filename));
// Cleanup
File::deleteDirectory($backupDir);
}
#[Test]
public function security_audit_finds_suspect_code()
{
$tempPluginPath = base_path('plugins/temp-audit-plugin');
if (!File::isDirectory($tempPluginPath)) {
File::makeDirectory($tempPluginPath, 0755, true);
}
File::put($tempPluginPath . '/unsafe.php', '<?php eval("echo 1;"); base64_decode("abc"); ?>');
$exitCode = Artisan::call('sw:plugins:audit', ['--path' => 'plugins/temp-audit-plugin']);
// It returns 1 if issues found
$this->assertEquals(1, $exitCode);
// Cleanup
File::deleteDirectory($tempPluginPath);
}
#[Test]
public function media_cleanup_removes_orphaned_files()
{
Storage::fake('public');
// 1. Create referenced media
$referencedMedia = Media::create([
'filename' => 'referenced.jpg',
'path' => 'media/referenced.jpg',
'mime_type' => 'image/jpeg',
'size' => 100,
]);
Storage::disk('public')->put('media/referenced.jpg', 'content');
Page::create([
'title' => 'Test Page',
'slug' => 'test',
'content' => [
['type' => 'media', 'media_id' => $referencedMedia->id]
],
'is_published' => true,
'user_id' => $this->user->id,
]);
// 2. Create orphaned media
$orphanedMedia = Media::create([
'filename' => 'orphaned.jpg',
'path' => 'media/orphaned.jpg',
'mime_type' => 'image/jpeg',
'size' => 100,
]);
Storage::disk('public')->put('media/orphaned.jpg', 'content');
// Verify they exist before cleanup
$this->assertDatabaseHas('media', ['id' => $referencedMedia->id]);
$this->assertDatabaseHas('media', ['id' => $orphanedMedia->id]);
Storage::disk('public')->assertExists('media/referenced.jpg');
Storage::disk('public')->assertExists('media/orphaned.jpg');
// 3. Run cleanup
Artisan::call('sw:media:cleanup');
// 4. Verify
$this->assertDatabaseHas('media', ['id' => $referencedMedia->id]);
$this->assertDatabaseMissing('media', ['id' => $orphanedMedia->id]);
Storage::disk('public')->assertExists('media/referenced.jpg');
Storage::disk('public')->assertMissing('media/orphaned.jpg');
}
}