test: full playlist export assertions
This commit is contained in:
parent
45221ced32
commit
c544f1db60
305
tests/Feature/FullPlaylistExportTest.php
Normal file
305
tests/Feature/FullPlaylistExportTest.php
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Label;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceAgendaItem;
|
||||
use App\Models\ServiceSong;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Slide;
|
||||
use App\Models\Song;
|
||||
use App\Services\PlaylistExportService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use ProPresenter\Parser\ProPlaylistReader;
|
||||
use Tests\TestCase;
|
||||
|
||||
final class FullPlaylistExportTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Storage::fake('public');
|
||||
}
|
||||
|
||||
public function test_full_service_playlist_includes_all_features_in_correct_order(): void
|
||||
{
|
||||
Setting::set('namenseinblender_macro_name', 'Namenseinblender');
|
||||
Setting::set('namenseinblender_macro_uuid', 'macro-uuid-1');
|
||||
Setting::set('namenseinblender_macro_collection_name', '--MAIN--');
|
||||
Setting::set('namenseinblender_macro_collection_uuid', 'collection-uuid-1');
|
||||
|
||||
Storage::disk('public')->put('slides/kv.jpg', 'keyvisual-image');
|
||||
Storage::disk('public')->put('slides/bg.jpg', 'background-image');
|
||||
Storage::disk('public')->put('slides/sermon1.jpg', 'sermon-image-1');
|
||||
Storage::disk('public')->put('slides/sermon2.jpg', 'sermon-image-2');
|
||||
|
||||
$service = Service::factory()->create([
|
||||
'title' => 'Voller Gottesdienst',
|
||||
'date' => now(),
|
||||
'key_visual_filename' => 'slides/kv.jpg',
|
||||
'background_filename' => 'slides/bg.jpg',
|
||||
'moderator_name' => 'Moderator Max',
|
||||
'preacher_name' => 'Pastor Paul',
|
||||
'preacher_name_override' => null,
|
||||
'has_agenda' => true,
|
||||
]);
|
||||
|
||||
$song = $this->createSongWithContent('Großer Gott');
|
||||
$serviceSong = ServiceSong::create([
|
||||
'service_id' => $service->id,
|
||||
'song_id' => $song->id,
|
||||
'cts_song_name' => 'Großer Gott',
|
||||
'order' => 1,
|
||||
]);
|
||||
ServiceAgendaItem::factory()->create([
|
||||
'service_id' => $service->id,
|
||||
'title' => 'Großer Gott',
|
||||
'service_song_id' => $serviceSong->id,
|
||||
'sort_order' => 1,
|
||||
'is_before_event' => false,
|
||||
]);
|
||||
|
||||
$sermonItem = ServiceAgendaItem::factory()->create([
|
||||
'service_id' => $service->id,
|
||||
'title' => 'Predigt',
|
||||
'service_song_id' => null,
|
||||
'sort_order' => 2,
|
||||
'is_before_event' => false,
|
||||
]);
|
||||
Slide::factory()->create([
|
||||
'service_id' => $service->id,
|
||||
'service_agenda_item_id' => $sermonItem->id,
|
||||
'type' => 'sermon',
|
||||
'original_filename' => 'sermon1.jpg',
|
||||
'stored_filename' => 'slides/sermon1.jpg',
|
||||
'sort_order' => 0,
|
||||
]);
|
||||
Slide::factory()->create([
|
||||
'service_id' => $service->id,
|
||||
'service_agenda_item_id' => $sermonItem->id,
|
||||
'type' => 'sermon',
|
||||
'original_filename' => 'sermon2.jpg',
|
||||
'stored_filename' => 'slides/sermon2.jpg',
|
||||
'sort_order' => 1,
|
||||
]);
|
||||
|
||||
$slideCountBefore = Slide::count();
|
||||
|
||||
$result = app(PlaylistExportService::class)->generatePlaylist($service);
|
||||
$playlist = ProPlaylistReader::read($result['path']);
|
||||
$names = array_map(fn ($entry) => $entry->getName(), $playlist->getEntries());
|
||||
|
||||
$moderatorIndex = array_search('Moderator', $names, true);
|
||||
$songIndex = array_search('Großer Gott', $names, true);
|
||||
$kvIndex = array_search('Keyvisual-Predigt', $names, true);
|
||||
$preacherIndex = array_search('Predigername', $names, true);
|
||||
$sermonIndex = array_search('Predigt', $names, true);
|
||||
|
||||
$this->assertNotFalse($moderatorIndex, 'Moderator nametag missing');
|
||||
$this->assertNotFalse($songIndex, 'Song presentation missing');
|
||||
$this->assertNotFalse($kvIndex, 'Keyvisual-Predigt entry missing');
|
||||
$this->assertNotFalse($preacherIndex, 'Predigername (preacher nametag) missing');
|
||||
$this->assertNotFalse($sermonIndex, 'Predigt (sermon slides) missing');
|
||||
|
||||
$this->assertSame(0, $moderatorIndex, 'Moderator nametag must be first');
|
||||
$this->assertLessThan($songIndex, $moderatorIndex);
|
||||
$this->assertLessThan($kvIndex, $songIndex);
|
||||
$this->assertLessThan($preacherIndex, $kvIndex, 'Keyvisual must come before preacher nametag');
|
||||
$this->assertLessThan($sermonIndex, $preacherIndex, 'Preacher nametag must come before sermon slides');
|
||||
|
||||
$songParser = $playlist->getEmbeddedSong('Großer Gott.pro');
|
||||
$this->assertNotNull($songParser, 'Embedded song .pro missing');
|
||||
$songSlides = $this->allParserSlides($songParser);
|
||||
$this->assertNotEmpty($songSlides);
|
||||
foreach ($songSlides as $slide) {
|
||||
$this->assertTrue($slide->hasBackgroundMedia(), 'Song slide must have background media');
|
||||
$this->assertSame(
|
||||
Storage::disk('public')->path('slides/bg.jpg'),
|
||||
$slide->getBackgroundMediaUrl(),
|
||||
);
|
||||
}
|
||||
|
||||
$sermonParser = $playlist->getEmbeddedSong('Predigt.pro');
|
||||
$this->assertNotNull($sermonParser, 'Embedded sermon .pro missing');
|
||||
$sermonSlides = $this->allParserSlides($sermonParser);
|
||||
$this->assertCount(2, $sermonSlides);
|
||||
foreach ($sermonSlides as $slide) {
|
||||
$this->assertTrue($slide->hasBackgroundMedia(), 'Sermon slide must have background media');
|
||||
}
|
||||
|
||||
$this->assertSame($slideCountBefore, Slide::count());
|
||||
|
||||
$this->cleanupTempDir($result['temp_dir']);
|
||||
}
|
||||
|
||||
public function test_without_macro_no_nametags_and_sermon_keeps_keyvisual_and_slides(): void
|
||||
{
|
||||
Setting::set('namenseinblender_macro_name', '');
|
||||
|
||||
Storage::disk('public')->put('slides/kv.jpg', 'keyvisual-image');
|
||||
Storage::disk('public')->put('slides/sermon1.jpg', 'sermon-image-1');
|
||||
Storage::disk('public')->put('slides/sermon2.jpg', 'sermon-image-2');
|
||||
|
||||
$service = Service::factory()->create([
|
||||
'title' => 'Ohne Macro',
|
||||
'date' => now(),
|
||||
'key_visual_filename' => 'slides/kv.jpg',
|
||||
'background_filename' => null,
|
||||
'moderator_name' => 'Moderator Max',
|
||||
'preacher_name' => 'Pastor Paul',
|
||||
'preacher_name_override' => null,
|
||||
'has_agenda' => true,
|
||||
]);
|
||||
|
||||
$sermonItem = ServiceAgendaItem::factory()->create([
|
||||
'service_id' => $service->id,
|
||||
'title' => 'Predigt',
|
||||
'service_song_id' => null,
|
||||
'sort_order' => 1,
|
||||
'is_before_event' => false,
|
||||
]);
|
||||
Slide::factory()->create([
|
||||
'service_id' => $service->id,
|
||||
'service_agenda_item_id' => $sermonItem->id,
|
||||
'type' => 'sermon',
|
||||
'original_filename' => 'sermon1.jpg',
|
||||
'stored_filename' => 'slides/sermon1.jpg',
|
||||
'sort_order' => 0,
|
||||
]);
|
||||
Slide::factory()->create([
|
||||
'service_id' => $service->id,
|
||||
'service_agenda_item_id' => $sermonItem->id,
|
||||
'type' => 'sermon',
|
||||
'original_filename' => 'sermon2.jpg',
|
||||
'stored_filename' => 'slides/sermon2.jpg',
|
||||
'sort_order' => 1,
|
||||
]);
|
||||
|
||||
$slideCountBefore = Slide::count();
|
||||
|
||||
$result = app(PlaylistExportService::class)->generatePlaylist($service);
|
||||
$playlist = ProPlaylistReader::read($result['path']);
|
||||
$names = array_map(fn ($entry) => $entry->getName(), $playlist->getEntries());
|
||||
|
||||
$this->assertNotContains('Moderator', $names);
|
||||
$this->assertNotContains('Predigername', $names);
|
||||
|
||||
$kvIndex = array_search('Keyvisual-Predigt', $names, true);
|
||||
$sermonIndex = array_search('Predigt', $names, true);
|
||||
$this->assertNotFalse($kvIndex, 'Keyvisual-Predigt entry missing');
|
||||
$this->assertNotFalse($sermonIndex, 'Predigt entry missing');
|
||||
$this->assertLessThan($sermonIndex, $kvIndex, 'Keyvisual must come before sermon slides');
|
||||
|
||||
$sermonParser = $playlist->getEmbeddedSong('Predigt.pro');
|
||||
$this->assertNotNull($sermonParser);
|
||||
$this->assertCount(2, $this->allParserSlides($sermonParser));
|
||||
|
||||
$this->assertSame($slideCountBefore, Slide::count());
|
||||
|
||||
$this->cleanupTempDir($result['temp_dir']);
|
||||
}
|
||||
|
||||
public function test_service_without_agenda_falls_back_to_legacy_export_without_crash(): void
|
||||
{
|
||||
Setting::set('namenseinblender_macro_name', 'Namenseinblender');
|
||||
|
||||
$service = Service::factory()->create([
|
||||
'title' => 'Ohne Agenda',
|
||||
'date' => now(),
|
||||
'key_visual_filename' => null,
|
||||
'background_filename' => null,
|
||||
'has_agenda' => false,
|
||||
]);
|
||||
|
||||
$song = $this->createSongWithContent('Legacy Lied');
|
||||
ServiceSong::create([
|
||||
'service_id' => $service->id,
|
||||
'song_id' => $song->id,
|
||||
'cts_song_name' => 'Legacy Lied',
|
||||
'order' => 1,
|
||||
]);
|
||||
|
||||
$slideCountBefore = Slide::count();
|
||||
|
||||
$result = app(PlaylistExportService::class)->generatePlaylist($service);
|
||||
|
||||
$this->assertArrayHasKey('path', $result);
|
||||
$this->assertArrayHasKey('filename', $result);
|
||||
$this->assertFileExists($result['path']);
|
||||
|
||||
$playlist = ProPlaylistReader::read($result['path']);
|
||||
$names = array_map(fn ($entry) => $entry->getName(), $playlist->getEntries());
|
||||
|
||||
$this->assertNotContains('Moderator', $names);
|
||||
$this->assertNotContains('Predigername', $names);
|
||||
$this->assertNotContains('Keyvisual-Predigt', $names);
|
||||
|
||||
$this->assertNotNull($playlist->getEmbeddedSong('Legacy Lied.pro'));
|
||||
|
||||
$this->assertSame($slideCountBefore, Slide::count());
|
||||
|
||||
$this->cleanupTempDir($result['temp_dir']);
|
||||
}
|
||||
|
||||
private function createSongWithContent(string $title): Song
|
||||
{
|
||||
$song = Song::create([
|
||||
'title' => $title,
|
||||
'ccli_id' => fake()->unique()->numerify('#####'),
|
||||
'author' => 'Test Author',
|
||||
'copyright_text' => 'Test Publisher',
|
||||
]);
|
||||
|
||||
$label = Label::firstOrCreate(
|
||||
['name' => 'Verse 1 - '.$title],
|
||||
['color' => '#2196F3'],
|
||||
);
|
||||
$label->songSlides()->create(['order' => 0, 'text_content' => 'Erste Zeile']);
|
||||
|
||||
$arrangement = $song->arrangements()->create(['name' => 'normal', 'is_default' => true]);
|
||||
$arrangement->arrangementLabels()->create(['label_id' => $label->id, 'order' => 0]);
|
||||
|
||||
return $song;
|
||||
}
|
||||
|
||||
private function allParserSlides(\ProPresenter\Parser\Song $parserSong): array
|
||||
{
|
||||
$slides = [];
|
||||
|
||||
foreach ($parserSong->getGroups() as $group) {
|
||||
foreach ($parserSong->getSlidesForGroup($group) as $slide) {
|
||||
$slides[] = $slide;
|
||||
}
|
||||
}
|
||||
|
||||
return $slides;
|
||||
}
|
||||
|
||||
private function cleanupTempDir(string $dir): void
|
||||
{
|
||||
if (! is_dir($dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$items = scandir($dir);
|
||||
if ($items === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item === '.' || $item === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = $dir.'/'.$item;
|
||||
is_dir($path) ? $this->cleanupTempDir($path) : unlink($path);
|
||||
}
|
||||
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue