- Added standard Laravel directory structure and configuration. - Included Svelte and Tailwind configuration for the admin interface. - Added core PHPUnit and testing scripts.
211 lines
6.5 KiB
PHP
211 lines
6.5 KiB
PHP
<?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');
|
|
}
|
|
}
|