- Fix probundle exports missing images (double slides/ prefix in storage path) - Replace manual ZipArchive with PresentationBundle + ProBundleWriter from parser plugin - Add per-agenda-item download route and buttons for songs and slide items - Remove text layer from image-only slides in .pro generation - Fix image conversion: upscale small images, black bars on 2 sides max (contain) - Add upload warnings for non-16:9 and sub-1920x1080 images (German, non-blocking) - Update SlideFactory and all tests to use slides/ prefix in stored_filename - Add 11 new tests (agenda download, image conversion, upload warnings)
233 lines
7.8 KiB
PHP
233 lines
7.8 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Service;
|
|
use App\Models\ServiceAgendaItem;
|
|
use App\Models\ServiceSong;
|
|
use App\Models\Slide;
|
|
use App\Models\Song;
|
|
use App\Models\SongGroup;
|
|
use App\Models\SongSlide;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
|
use Tests\TestCase;
|
|
use ZipArchive;
|
|
|
|
final class AgendaItemDownloadTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_agenda_item_mit_slides_liefert_probundle(): void
|
|
{
|
|
Storage::fake('public');
|
|
|
|
$user = User::factory()->create();
|
|
$service = Service::factory()->create();
|
|
|
|
$agendaItem = ServiceAgendaItem::factory()->create([
|
|
'service_id' => $service->id,
|
|
'title' => 'Predigt',
|
|
'sort_order' => 1,
|
|
'service_song_id' => null,
|
|
'is_before_event' => false,
|
|
]);
|
|
|
|
Storage::disk('public')->put('slides/predigt-1.jpg', 'fake-image-1');
|
|
Storage::disk('public')->put('slides/predigt-2.jpg', 'fake-image-2');
|
|
|
|
Slide::factory()->create([
|
|
'service_id' => $service->id,
|
|
'service_agenda_item_id' => $agendaItem->id,
|
|
'type' => 'sermon',
|
|
'stored_filename' => 'slides/predigt-1.jpg',
|
|
'original_filename' => 'Predigt Folie 1.jpg',
|
|
'sort_order' => 0,
|
|
]);
|
|
|
|
Slide::factory()->create([
|
|
'service_id' => $service->id,
|
|
'service_agenda_item_id' => $agendaItem->id,
|
|
'type' => 'sermon',
|
|
'stored_filename' => 'slides/predigt-2.jpg',
|
|
'original_filename' => 'Predigt Folie 2.jpg',
|
|
'sort_order' => 1,
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->get(
|
|
route('services.agenda-item.download', [
|
|
'service' => $service,
|
|
'agendaItem' => $agendaItem,
|
|
])
|
|
);
|
|
|
|
$response->assertOk();
|
|
$response->assertHeader('content-type', 'application/zip');
|
|
|
|
$baseResponse = $response->baseResponse;
|
|
$this->assertInstanceOf(BinaryFileResponse::class, $baseResponse);
|
|
|
|
$copiedPath = sys_get_temp_dir().'/agenda-test-'.uniqid().'.probundle';
|
|
copy($baseResponse->getFile()->getPathname(), $copiedPath);
|
|
|
|
$zip = new ZipArchive;
|
|
$this->assertTrue($zip->open($copiedPath) === true);
|
|
$this->assertSame(3, $zip->numFiles);
|
|
|
|
$names = [];
|
|
for ($i = 0; $i < $zip->numFiles; $i++) {
|
|
$name = $zip->getNameIndex($i);
|
|
if ($name !== false) {
|
|
$names[] = $name;
|
|
}
|
|
}
|
|
|
|
$proEntry = array_values(array_filter($names, fn (string $n) => str_ends_with($n, '.pro')));
|
|
$this->assertCount(1, $proEntry, '.probundle muss genau eine .pro Datei enthalten');
|
|
$this->assertContains('predigt-1.jpg', $names);
|
|
$this->assertContains('predigt-2.jpg', $names);
|
|
|
|
$this->assertSame('fake-image-1', $zip->getFromName('predigt-1.jpg'));
|
|
$this->assertSame('fake-image-2', $zip->getFromName('predigt-2.jpg'));
|
|
|
|
$zip->close();
|
|
@unlink($copiedPath);
|
|
}
|
|
|
|
public function test_song_agenda_item_liefert_probundle_mit_pro_daten(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$service = Service::factory()->create();
|
|
|
|
$song = Song::factory()->create(['title' => 'Amazing Grace']);
|
|
$group = SongGroup::factory()->create(['song_id' => $song->id, 'name' => 'Verse 1', 'order' => 1]);
|
|
SongSlide::factory()->create(['song_group_id' => $group->id, 'text_content' => 'Amazing grace how sweet the sound', 'order' => 1]);
|
|
|
|
$serviceSong = ServiceSong::create([
|
|
'service_id' => $service->id,
|
|
'song_id' => $song->id,
|
|
'song_arrangement_id' => null,
|
|
'use_translation' => false,
|
|
'order' => 1,
|
|
'cts_song_name' => 'Amazing Grace',
|
|
'cts_ccli_id' => '12345',
|
|
]);
|
|
|
|
$agendaItem = ServiceAgendaItem::factory()->create([
|
|
'service_id' => $service->id,
|
|
'title' => 'Amazing Grace',
|
|
'sort_order' => 1,
|
|
'service_song_id' => $serviceSong->id,
|
|
'is_before_event' => false,
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->get(
|
|
route('services.agenda-item.download', [
|
|
'service' => $service,
|
|
'agendaItem' => $agendaItem,
|
|
])
|
|
);
|
|
|
|
$response->assertOk();
|
|
$response->assertHeader('content-type', 'application/zip');
|
|
|
|
$baseResponse = $response->baseResponse;
|
|
$this->assertInstanceOf(BinaryFileResponse::class, $baseResponse);
|
|
|
|
$copiedPath = sys_get_temp_dir().'/agenda-song-test-'.uniqid().'.probundle';
|
|
copy($baseResponse->getFile()->getPathname(), $copiedPath);
|
|
|
|
$zip = new ZipArchive;
|
|
$this->assertTrue($zip->open($copiedPath) === true);
|
|
$this->assertSame(1, $zip->numFiles);
|
|
$this->assertTrue(str_ends_with($zip->getNameIndex(0), '.pro'), '.probundle muss eine .pro Datei enthalten');
|
|
|
|
$proContent = $zip->getFromName($zip->getNameIndex(0));
|
|
$this->assertNotFalse($proContent);
|
|
$this->assertGreaterThan(0, strlen($proContent));
|
|
|
|
$zip->close();
|
|
@unlink($copiedPath);
|
|
}
|
|
|
|
public function test_agenda_item_von_anderem_service_liefert_404(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$service = Service::factory()->create();
|
|
$otherService = Service::factory()->create();
|
|
|
|
$agendaItem = ServiceAgendaItem::factory()->create([
|
|
'service_id' => $otherService->id,
|
|
'title' => 'Fremd',
|
|
'sort_order' => 1,
|
|
'is_before_event' => false,
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->get(
|
|
route('services.agenda-item.download', [
|
|
'service' => $service,
|
|
'agendaItem' => $agendaItem,
|
|
])
|
|
);
|
|
|
|
$response->assertNotFound();
|
|
}
|
|
|
|
public function test_agenda_item_ohne_slides_und_ohne_song_liefert_leeres_probundle(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$service = Service::factory()->create();
|
|
|
|
$agendaItem = ServiceAgendaItem::factory()->create([
|
|
'service_id' => $service->id,
|
|
'title' => 'Leer',
|
|
'sort_order' => 1,
|
|
'service_song_id' => null,
|
|
'is_before_event' => false,
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->get(
|
|
route('services.agenda-item.download', [
|
|
'service' => $service,
|
|
'agendaItem' => $agendaItem,
|
|
])
|
|
);
|
|
|
|
$response->assertOk();
|
|
|
|
$baseResponse = $response->baseResponse;
|
|
$this->assertInstanceOf(BinaryFileResponse::class, $baseResponse);
|
|
|
|
$copiedPath = sys_get_temp_dir().'/agenda-empty-test-'.uniqid().'.probundle';
|
|
copy($baseResponse->getFile()->getPathname(), $copiedPath);
|
|
|
|
$zip = new ZipArchive;
|
|
$this->assertTrue($zip->open($copiedPath) === true);
|
|
$this->assertSame(1, $zip->numFiles);
|
|
$this->assertTrue(str_ends_with($zip->getNameIndex(0), '.pro'), 'Einziger Eintrag muss .pro Datei sein');
|
|
|
|
$zip->close();
|
|
@unlink($copiedPath);
|
|
}
|
|
|
|
public function test_erfordert_authentifizierung(): void
|
|
{
|
|
$service = Service::factory()->create();
|
|
$agendaItem = ServiceAgendaItem::factory()->create([
|
|
'service_id' => $service->id,
|
|
'is_before_event' => false,
|
|
]);
|
|
|
|
$response = $this->get(
|
|
route('services.agenda-item.download', [
|
|
'service' => $service,
|
|
'agendaItem' => $agendaItem,
|
|
])
|
|
);
|
|
|
|
$response->assertRedirect(route('login'));
|
|
}
|
|
}
|