Browse and clean up the media library through the API
Some checks failed
Tests / lint (push) Has been cancelled
Tests / test (push) Has been cancelled
Tests / test-postgres (push) Has been cancelled
Tests / mcp (push) Has been cancelled

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 19:55:03 +02:00
parent 8d0f2e3639
commit 4c4a2b3299
10 changed files with 342 additions and 4 deletions

View File

@@ -0,0 +1,133 @@
<?php
declare(strict_types=1);
namespace Tests\Feature\Api;
use App\Jobs\ProcessMedia;
use App\Models\Media;
use App\Models\Page;
use App\Models\Technology;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use PHPUnit\Framework\Attributes\Test;
use Tests\FakesPireon;
use Tests\TestCase;
/**
* The media library through the admin API: what is on the disk, what uses it,
* and deleting what nothing does.
*/
class MediaLibraryApiTest extends TestCase
{
use FakesPireon, RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->seed(RolePermissionSeeder::class);
Storage::fake((string) config('media.disk'));
Queue::fake();
}
protected function actingWithRole(string $role): User
{
$user = User::factory()->create();
$user->syncRoles([$role]);
$this->withHeaders($this->apiHeadersFor($user));
return $user;
}
protected function stored(string $path): Media
{
Storage::disk((string) config('media.disk'))->put($path, 'x');
return Media::for($path);
}
#[Test]
public function the_library_says_what_is_in_use(): void
{
$this->actingWithRole('editor');
$logo = $this->stored('technologies/angular.svg');
$orphan = $this->stored('pages/forgotten.jpg');
Technology::factory()->create(['logo' => $logo->path]);
$this->getJson('/api/v1/media')
->assertOk()
->assertJsonCount(2, 'data')
->assertJsonPath('data.0.path', 'pages/forgotten.jpg')
->assertJsonPath('data.0.referenced', false)
->assertJsonPath('data.1.referenced', true);
$this->getJson('/api/v1/media?orphans=1')
->assertJsonCount(1, 'data')
->assertJsonPath('data.0.id', $orphan->id);
$this->getJson('/api/v1/media?for=technologies')
->assertJsonCount(1, 'data')
->assertJsonPath('data.0.id', $logo->id);
}
#[Test]
public function an_image_nothing_uses_is_deleted_with_its_file(): void
{
$this->actingWithRole('editor');
$orphan = $this->stored('pages/forgotten.jpg');
$this->deleteJson("/api/v1/media/{$orphan->id}")->assertNoContent();
$this->assertModelMissing($orphan);
Storage::disk((string) config('media.disk'))->assertMissing('pages/forgotten.jpg');
}
#[Test]
public function an_image_in_use_is_not_deleted(): void
{
$this->actingWithRole('editor');
$image = $this->stored('pages/hero.jpg');
Page::factory()->create(['featured_image' => $image->path]);
$this->deleteJson("/api/v1/media/{$image->id}")->assertConflict();
$this->assertModelExists($image);
Storage::disk((string) config('media.disk'))->assertExists('pages/hero.jpg');
}
#[Test]
public function a_writer_sees_the_library_but_deletes_nothing(): void
{
$this->actingWithRole('writer');
$orphan = $this->stored('pages/forgotten.jpg');
$this->getJson('/api/v1/media')->assertOk();
$this->getJson("/api/v1/media/{$orphan->id}")->assertOk()->assertJsonPath('data.referenced', false);
$this->deleteJson("/api/v1/media/{$orphan->id}")->assertForbidden();
}
#[Test]
public function an_image_is_processed_again_on_request(): void
{
$this->actingWithRole('editor');
$image = $this->stored('pages/hero.jpg');
Queue::fake();
$this->postJson("/api/v1/media/{$image->id}/reprocess")->assertAccepted();
Queue::assertPushed(ProcessMedia::class);
}
}