Files
panel-pelican-dev/tests/Integration/Services/Backups/DeleteBackupServiceTest.php
Lance Pioch 0579fd6df5 Use presigned URLs for all S3 operations (#1022)
Some S3-compatible providers (e.g., Hetzner) require presigned requests.
Previously only GetObject and UploadPart were presigned; the remaining
operations (CreateMultipartUpload, ListParts, CompleteMultipartUpload,
AbortMultipartUpload, DeleteObject) used header-based auth. This adds an
executeS3Command() method to S3Filesystem that sends all commands via
presigned URLs with manual XML response parsing, giving universal
compatibility with all S3-compatible providers.
2026-02-06 11:16:12 -05:00

109 lines
3.6 KiB
PHP

<?php
namespace App\Tests\Integration\Services\Backups;
use App\Exceptions\Service\Backup\BackupLockedException;
use App\Extensions\Backups\BackupManager;
use App\Extensions\Filesystem\S3Filesystem;
use App\Models\Backup;
use App\Repositories\Daemon\DaemonBackupRepository;
use App\Services\Backups\DeleteBackupService;
use App\Tests\Integration\IntegrationTestCase;
use Aws\CommandInterface;
use GuzzleHttp\Psr7\Response;
use Illuminate\Http\Client\ConnectionException;
use Mockery;
class DeleteBackupServiceTest extends IntegrationTestCase
{
public function test_locked_backup_cannot_be_deleted(): void
{
$server = $this->createServerModel();
$backup = Backup::factory()->create([
'server_id' => $server->id,
'is_locked' => true,
]);
$this->expectException(BackupLockedException::class);
$this->app->make(DeleteBackupService::class)->handle($backup);
}
public function test_failed_backup_that_is_locked_can_be_deleted(): void
{
$server = $this->createServerModel();
$backup = Backup::factory()->create([
'server_id' => $server->id,
'is_locked' => true,
'is_successful' => false,
]);
$mock = $this->mock(DaemonBackupRepository::class);
$mock->expects('setServer->delete')->with($backup)->andReturn(new Response());
$this->app->make(DeleteBackupService::class)->handle($backup);
$backup->refresh();
$this->assertNotNull($backup->deleted_at);
}
public function test_exception_thrown_due_to_missing_backup_is_ignored(): void
{
$server = $this->createServerModel();
$backup = Backup::factory()->create(['server_id' => $server->id]);
$mock = $this->mock(DaemonBackupRepository::class);
$mock->expects('setServer->delete')->with($backup)->andThrow(new ConnectionException(code: 404));
$this->app->make(DeleteBackupService::class)->handle($backup);
$backup->refresh();
$this->assertNotNull($backup->deleted_at);
}
public function test_exception_is_thrown_if_not404(): void
{
$server = $this->createServerModel();
$backup = Backup::factory()->create(['server_id' => $server->id]);
$mock = $this->mock(DaemonBackupRepository::class);
$mock->expects('setServer->delete')->with($backup)->andThrow(new ConnectionException(code: 500));
$this->expectException(ConnectionException::class);
$this->app->make(DeleteBackupService::class)->handle($backup);
$backup->refresh();
$this->assertNull($backup->deleted_at);
}
public function test_s3_object_can_be_deleted(): void
{
$server = $this->createServerModel();
$backup = Backup::factory()->create([
'disk' => Backup::ADAPTER_AWS_S3,
'server_id' => $server->id,
]);
$manager = $this->mock(BackupManager::class);
$adapter = $this->mock(S3Filesystem::class);
$manager->expects('adapter')->with(Backup::ADAPTER_AWS_S3)->andReturn($adapter);
$adapter->expects('getBucket')->andReturn('foobar');
$mockCommand = Mockery::mock(CommandInterface::class);
$adapter->expects('getClient->getCommand')->with('DeleteObject', [
'Bucket' => 'foobar',
'Key' => sprintf('%s/%s.tar.gz', $server->uuid, $backup->uuid),
])->andReturn($mockCommand);
$adapter->expects('executeS3Command')->with($mockCommand);
$this->app->make(DeleteBackupService::class)->handle($backup);
$this->assertSoftDeleted($backup);
}
}