- 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"
88 lines
2.9 KiB
PHP
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);
|
|
});
|