- 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>
93 lines
4.1 KiB
PHP
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);
|
|
});
|