Initial commit: Pireon Admin — headless admin panel and public API

Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer
collective, backed by the Pireon OAuth identity server. The public site is a
separate, server-rendered application consuming /api/public/v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 17:55:27 +02:00
commit 3897113313
534 changed files with 64050 additions and 0 deletions

View File

@@ -0,0 +1,153 @@
<?php
declare(strict_types=1);
namespace Tests\Feature\Media;
use App\Filament\Resources\Projects\ProjectResource;
use App\Models\ImageAttachment;
use App\Models\Media;
use App\Models\Project;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
use PHPUnit\Framework\Attributes\Test;
use Tests\FakesPireon;
use Tests\TestCase;
/**
* More than one image on a project: the screenshots and the before-and-after
* that make a write-up worth reading.
*/
class ProjectGalleryTest extends TestCase
{
use FakesPireon, RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->withHeaders(['Accept-Language' => 'es']);
Queue::fake();
}
protected function projectWithGallery(): Project
{
$project = Project::factory()->published()->create([
'title' => ['es' => 'Mi proyecto'],
'slug' => ['es' => 'mi-proyecto'],
]);
$project->images()->createMany([
[
'path' => 'projects/gallery/after.jpg',
'alt' => ['es' => 'El panel nuevo'],
'caption' => ['es' => 'Después'],
'position' => 2,
],
[
'path' => 'projects/gallery/before.jpg',
'alt' => ['es' => 'La hoja de cálculo que usaban'],
'caption' => ['es' => 'Antes'],
'position' => 1,
],
]);
return $project;
}
#[Test]
public function the_gallery_comes_back_in_the_order_it_was_arranged(): void
{
$this->projectWithGallery();
$gallery = $this->getJson('/api/public/v1/projects/mi-proyecto')
->assertOk()
->json('data.gallery');
// An unordered gallery is a different gallery every time it is read,
// and "before" after "after" tells the wrong story.
$this->assertSame(['Antes', 'Después'], array_column($gallery, 'caption'));
}
#[Test]
public function each_image_carries_everything_needed_to_render_it(): void
{
$this->projectWithGallery();
Media::query()->where('path', 'projects/gallery/before.jpg')->sole()->forceFill([
'width' => 1200,
'height' => 800,
'variants' => [
['format' => 'webp', 'width' => 400, 'path' => 'projects/gallery/before-400.webp', 'size' => 8000],
],
'processed_at' => now(),
])->save();
$first = $this->getJson('/api/public/v1/projects/mi-proyecto')
->assertOk()
->json('data.gallery.0');
$this->assertStringStartsWith('http', $first['url']);
$this->assertSame(1200, $first['width']);
$this->assertSame('La hoja de cálculo que usaban', $first['alt']);
$this->assertStringContainsString('400w', $first['srcset']['webp']);
// A caption is not a description: one is shown to everyone, the other
// is read by whoever cannot see the image.
$this->assertSame('Antes', $first['caption']);
}
#[Test]
public function a_listing_does_not_carry_every_screenshot_of_every_project(): void
{
$this->projectWithGallery();
$project = $this->getJson('/api/public/v1/projects')
->assertOk()
->json('data.0');
$this->assertArrayNotHasKey('gallery', $project);
}
#[Test]
public function adding_a_gallery_image_registers_it_like_any_other_upload(): void
{
$project = Project::factory()->create();
$project->images()->create(['path' => 'projects/gallery/new.jpg']);
// Nothing special about a gallery image: same media row, same derived
// versions, same queue.
$this->assertSame(1, Media::query()->where('path', 'projects/gallery/new.jpg')->count());
}
#[Test]
public function erasing_a_project_for_good_takes_its_gallery_with_it(): void
{
$project = $this->projectWithGallery();
$project->delete();
// A soft delete keeps it — that is the point of a soft delete.
$this->assertSame(2, ImageAttachment::query()->count());
$project->forceDelete();
// A morph relation has no foreign key to cascade for it, so this is
// handled explicitly and worth pinning down.
$this->assertSame(0, ImageAttachment::query()->count());
}
#[Test]
public function the_gallery_editor_renders(): void
{
$this->seed(RolePermissionSeeder::class);
$project = $this->projectWithGallery();
$this->actingAs($this->userWithRoles('admin'));
$this->get(ProjectResource::getUrl('edit', ['record' => $project]))
->assertSuccessful();
}
}