presseportale/tests/Feature/Legal/LegalTextTest.php
Kevin Adametz 214aea8910 images: Datenmodell, Textarchiv, Herkunftsprüfung und gemeinsamer Uploader für Formular v2 (Bildkennzeichnung Block A)
- press_release_images: Kundenangabe/Portalkennzeichnung getrennt, Herkunftsbefund, Beweisoriginal, Snapshot-Pfade, published_ever_at, rules_version, Fokus/Letterbox; Bestand unangetastet
- legal_texts (insert-only) mit img-confirm-v1 (alter Wortlaut), v2 (AGB 0.6), v3 (AGB 0.7, noch ohne Datum); image-rights:backfill-confirmations
- image_rights_confirmations polymorph, insert-only, IP als versionierter HMAC; Löschung nur für nie veröffentlichte Uploads
- ImageProvenanceInspector (Imagick/exif): IPTC, XMP, EXIF; algorithmicallyEnhanced nur Vermerk; unlesbare Datei = error
- PressReleaseImageUploader: Original vor Derivaten, Hash-Bindung, Titelbild-Regel unter Sperre, Dateien bei Fehlern zurückgerollt; Admin-Upload ohne Bestätigung
- ImageService: kein Rückfall auf die Quelldatei, og-Variante, Fokuspunkt und no_crop

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 09:22:05 +00:00

93 lines
4.1 KiB
PHP

<?php
use App\Models\ImageRightsConfirmation;
use App\Models\LegalDocument;
use App\Models\LegalText;
use App\Models\PressRelease;
use App\Models\PressReleaseImage;
use App\Services\Image\ImageRightsTextRenderer;
use Database\Seeders\LegalDocumentSeeder;
use Database\Seeders\LegalTextSeeder;
use Tests\TestCase;
test('the seeder registers the three confirmation texts idempotently and never rewrites one', function () {
/** @var TestCase $this */
$this->seed(LegalTextSeeder::class);
$this->seed(LegalTextSeeder::class);
expect(LegalText::query()->count())->toBe(3);
$v2 = LegalText::findByKey('img-confirm-v2');
expect($v2?->body_sha256)->toBe(hash('sha256', (string) $v2?->body))
->and($v2?->bound_agb_version)->toBe('0.6')
->and(LegalText::findByKey('img-confirm-v3')?->effective_from)->toBeNull();
});
test('currentFor picks the text bound to the active agb version and ignores fassungen without a date', function () {
/** @var TestCase $this */
$this->seed(LegalTextSeeder::class);
expect(LegalText::currentFor(LegalText::TYPE_IMAGE_RIGHTS_CONFIRMATION, '0.6')?->key)->toBe('img-confirm-v2')
->and(LegalText::currentFor(LegalText::TYPE_IMAGE_RIGHTS_CONFIRMATION, '0.7'))->toBeNull()
->and(LegalText::findByKey('img-confirm-v2')?->isCurrentFor('0.6'))->toBeTrue()
->and(LegalText::findByKey('img-confirm-v1')?->isCurrentFor('0.6'))->toBeFalse();
});
test('legal texts are immutable', function () {
/** @var TestCase $this */
$this->seed(LegalTextSeeder::class);
$text = LegalText::findByKey('img-confirm-v2');
expect(fn () => $text->update(['body' => 'geändert']))->toThrow(LogicException::class)
->and(fn () => $text->delete())->toThrow(LogicException::class);
});
test('the renderer resolves agb version, date and url into the long and short wording', function () {
/** @var TestCase $this */
$this->seed(LegalDocumentSeeder::class);
$this->seed(LegalTextSeeder::class);
$renderer = app(ImageRightsTextRenderer::class);
$text = $renderer->current();
expect($text?->key)->toBe('img-confirm-v2');
$long = $renderer->renderLong($text, 'https://presseecho.test/de/agb');
$short = $renderer->renderShort($text, 'https://presseecho.test/de/agb');
expect($long)->toContain('Fassung vom 3. Juli 2026 (Version 0.6, https://presseecho.test/de/agb)')
->toContain('presseecho.de und businessportal24.com')
->toContain('§ 8 und § 9')
->not->toContain('{agb_version}')
->and($short)->toContain('Fassung vom 3. Juli 2026')
->and(LegalDocument::currentAgbVersion())->toBe('0.6');
});
test('the backfill command gives every old confirmation a protocol row pointing at img-confirm-v1', function () {
/** @var TestCase $this */
$this->seed(LegalTextSeeder::class);
$pressRelease = PressRelease::factory()->create();
$confirmed = PressReleaseImage::factory()->for($pressRelease)->create(['rights_confirmed_at' => now()->subMonth()]);
PressReleaseImage::factory()->for($pressRelease)->legacy()->create();
$this->artisan('image-rights:backfill-confirmations', ['--dry-run' => true])
->expectsOutputToContain('Bilder mit alter Bestätigung ohne Protokoll: 1')
->assertSuccessful();
expect(ImageRightsConfirmation::query()->count())->toBe(0);
$this->artisan('image-rights:backfill-confirmations')->assertSuccessful();
$this->artisan('image-rights:backfill-confirmations')->assertSuccessful();
$confirmation = $confirmed->rightsConfirmations()->sole();
expect(ImageRightsConfirmation::query()->count())->toBe(1)
->and($confirmation->legal_text_key)->toBe('img-confirm-v1')
->and($confirmation->agb_version)->toBe('0.6')
->and($confirmation->user_id)->toBe($pressRelease->user_id)
->and($confirmation->company_id)->toBe($pressRelease->company_id)
->and($confirmation->confirmed_at->toDateTimeString())->toBe($confirmed->rights_confirmed_at->toDateTimeString())
->and($confirmation->declaration_snapshot['backfilled'])->toBeTrue()
->and($confirmation->text_snapshot)->toBe(LegalText::findByKey('img-confirm-v1')?->body);
});