pp-planer/tests/Feature/CcliEmptySongImportTest.php
Thorsten Bus 42b8b5f428 fix(export): copyright/blank slides, bundle-relative media, probundle injection, song links
- populate COPYRIGHT (title/author/copyright/CCLI) + blank slides on every song; songHasContent ignores locked sections
- foreground info/moderation images now bundle-relative (fixes blank images)
- pre-added .probundle injection: Zip64-fix + verbatim .pro extraction (fixes empty bundle)
- nametag subtitle split (text + subtitle); smaller non-bold render
- skip songs with no content slides at export with German warning
- link service agenda songs to SongDB edit modal via #song-<id>
- allow CCLI import of metadata-only songs (no lyric sections)
- expose has_content_slides on service songs; show "Keine Inhaltsfolien"
2026-06-21 09:58:55 +02:00

88 lines
2.9 KiB
PHP

<?php
use App\Models\Setting;
use App\Models\Song;
use App\Models\SongSlide;
use App\Services\CcliImportService;
use App\Services\CcliPasteParser;
use App\Services\DTO\ParsedCcliSong;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
/**
* A real CCLI page that carries only metadata (title + CCLI number) but no
* lyric/section labels — e.g. an instrumental or a stub song.
*/
function metadataOnlyPaste(): string
{
return <<<'TXT'
Instrumental Intro
© 2024 Test Publishing House
CCLI # 9999500
TXT;
}
test('parser akzeptiert Metadaten-only Paste ohne zu werfen und liefert leere Sektionen', function (): void {
$parser = new CcliPasteParser;
$result = $parser->parse(metadataOnlyPaste());
expect($result)->toBeInstanceOf(ParsedCcliSong::class)
->and($result->title)->toBe('Instrumental Intro')
->and($result->ccliId)->toBe('9999500')
->and($result->year)->toBe('2024')
->and($result->sections)->toBe([]);
});
test('parser wirft weiterhin wenn weder Titel noch CCLI vorhanden', function (): void {
$parser = new CcliPasteParser;
expect(fn () => $parser->parse(" \n \n"))
->toThrow(InvalidArgumentException::class);
});
test('import von Metadaten-only erzeugt Song ohne Inhalts-Sektionen aber mit Copyright+Blank', function (): void {
$service = app(CcliImportService::class);
$result = $service->import(metadataOnlyPaste());
$song = $result['song']->fresh();
expect($result['status'])->toBe('created')
->and($song->ccli_id)->toBe('9999500')
->and($song->has_translation)->toBeFalse();
// normal-Arrangement existiert trotz fehlender Inhalts-Sektionen.
$arrangement = $song->arrangements()->where('name', 'normal')->first();
expect($arrangement)->not->toBeNull()
->and($arrangement->is_default)->toBeTrue();
// Genau zwei Folien: eine COPYRIGHT-Folie, eine BLANK-Folie.
expect(SongSlide::count())->toBe(2);
$prefixLabelId = (int) Setting::get('song_prefix_label_id');
$copyrightSlide = $song->sections()
->where('label_id', $prefixLabelId)
->first()
->slides()
->first();
expect($copyrightSlide->text_content)->toContain('Instrumental Intro')
->and($copyrightSlide->text_content)->toContain('CCLI-Liednr. 9999500');
});
test('songHasContent bleibt false fuer Metadaten-only Song (Duplikat-Guard behandelt als leer)', function (): void {
$service = app(CcliImportService::class);
// Erster Import legt den leeren Song an.
$service->import(metadataOnlyPaste());
// Zweiter Import darf NICHT als Duplikat geworfen werden, da kein Inhalt.
// Status 'restored' (Song existiert), aber keine DuplicateCcliSongException.
$result = $service->import(metadataOnlyPaste());
expect($result['status'])->toBe('restored')
->and(Song::where('ccli_id', '9999500')->count())->toBe(1);
});