167 lines
5.7 KiB
PHP
167 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\ExportProFile;
|
|
use App\Models\Label;
|
|
use App\Models\Service;
|
|
use App\Models\ServiceAgendaItem;
|
|
use App\Models\ServiceSong;
|
|
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 ExportProFileKeywordInjectionTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
Storage::fake('local');
|
|
Storage::fake('public');
|
|
}
|
|
|
|
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 - '.$title],
|
|
['color' => '#2196F3'],
|
|
);
|
|
$section = $song->sections()->create(['label_id' => $label->id, 'order' => 0]);
|
|
$section->slides()->create(['order' => 0, 'text_content' => 'Erste Zeile']);
|
|
|
|
$arrangement = $song->arrangements()->create(['name' => 'normal', 'is_default' => true]);
|
|
$arrangement->arrangementSections()->create(['song_section_id' => $section->id, 'order' => 0]);
|
|
|
|
return $song;
|
|
}
|
|
|
|
private function addSongAgendaItem(Service $service, Song $song, string $title, int $sortOrder): void
|
|
{
|
|
$serviceSong = ServiceSong::create([
|
|
'service_id' => $service->id,
|
|
'song_id' => $song->id,
|
|
'cts_song_name' => $song->title,
|
|
'order' => $sortOrder,
|
|
]);
|
|
|
|
ServiceAgendaItem::factory()->create([
|
|
'service_id' => $service->id,
|
|
'title' => $title,
|
|
'service_song_id' => $serviceSong->id,
|
|
'sort_order' => $sortOrder,
|
|
'is_before_event' => false,
|
|
]);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
public function test_keyword_datei_wird_direkt_nach_passendem_ablaufpunkt_eingefuegt(): void
|
|
{
|
|
$service = Service::factory()->create([
|
|
'title' => 'Keyword Service',
|
|
'date' => now(),
|
|
]);
|
|
|
|
// Uppercase title vs lowercase keyword proves case-insensitive substring match.
|
|
$this->addSongAgendaItem($service, $this->createSongWithContent('Abendmahlslied'), 'ABENDMAHL feiern', 1);
|
|
$this->addSongAgendaItem($service, $this->createSongWithContent('Predigtlied'), 'Predigt', 2);
|
|
|
|
$storedPath = 'export-pro-files/keyword/kommunion.pro';
|
|
Storage::disk('local')->put($storedPath, 'kommunion-pro-bytes');
|
|
|
|
ExportProFile::create([
|
|
'type' => 'keyword',
|
|
'keyword' => 'abendmahl',
|
|
'original_name' => 'kommunion.pro',
|
|
'stored_path' => $storedPath,
|
|
'order' => 1,
|
|
]);
|
|
|
|
$result = app(PlaylistExportService::class)->generatePlaylist($service);
|
|
$playlist = ProPlaylistReader::read($result['path']);
|
|
|
|
$entryNames = array_map(fn ($e) => $e->getName(), $playlist->getEntries());
|
|
|
|
$abendmahlPos = array_search('Abendmahlslied', $entryNames, true);
|
|
$predigtPos = array_search('Predigtlied', $entryNames, true);
|
|
$kommunionPositions = array_keys($entryNames, 'kommunion', true);
|
|
|
|
$this->assertNotFalse($abendmahlPos, 'Abendmahl song entry not found');
|
|
$this->assertNotFalse($predigtPos, 'Predigt song entry not found');
|
|
$this->assertCount(1, $kommunionPositions, 'Keyword presentation must be injected exactly once');
|
|
|
|
$kommunionPos = $kommunionPositions[0];
|
|
$this->assertSame($abendmahlPos + 1, $kommunionPos, 'Keyword presentation must appear immediately after the matched item');
|
|
$this->assertLessThan($predigtPos, $kommunionPos, 'Keyword presentation must not appear after the non-matching item');
|
|
|
|
$embeddedFiles = $playlist->getEmbeddedFiles();
|
|
$this->assertArrayHasKey('KEYWORD_1_kommunion.pro', $embeddedFiles);
|
|
$this->assertSame('kommunion-pro-bytes', $embeddedFiles['KEYWORD_1_kommunion.pro']);
|
|
|
|
$this->cleanupTempDir($result['temp_dir']);
|
|
}
|
|
|
|
public function test_leeres_keyword_wird_nicht_injiziert(): void
|
|
{
|
|
$service = Service::factory()->create([
|
|
'title' => 'Kein Keyword Service',
|
|
'date' => now(),
|
|
]);
|
|
|
|
$this->addSongAgendaItem($service, $this->createSongWithContent('Abendmahlslied'), 'Abendmahl feiern', 1);
|
|
|
|
$storedPath = 'export-pro-files/keyword/leer.pro';
|
|
Storage::disk('local')->put($storedPath, 'leer-pro-bytes');
|
|
|
|
ExportProFile::create([
|
|
'type' => 'keyword',
|
|
'keyword' => '',
|
|
'original_name' => 'leer.pro',
|
|
'stored_path' => $storedPath,
|
|
'order' => 1,
|
|
]);
|
|
|
|
$result = app(PlaylistExportService::class)->generatePlaylist($service);
|
|
$playlist = ProPlaylistReader::read($result['path']);
|
|
|
|
$entryNames = array_map(fn ($e) => $e->getName(), $playlist->getEntries());
|
|
|
|
$this->assertNotContains('leer', $entryNames, 'Files with an empty keyword must never be injected');
|
|
|
|
$this->cleanupTempDir($result['temp_dir']);
|
|
}
|
|
}
|