press-releases: Publish-Rechte, Quota-Races & Slug vereinheitlichen

- Publish/Reject/Archive und changeStatus erzwingen konsistent
  press-releases:publish; changeStatus schloss die Lücke, über die das
  Status-Modal die Prüfung umging (Fix#1/H2).
- publish()/changeStatusFromAdmin sperren PM- und User-Zeile
  transaktional; QuotaExceededException wird in Admin-Publish und im
  Auto-Publish-Job abgefangen (Warnung statt 500 / bleibt in Review, Fix#3/M2).
- Slug-Erzeugung nur noch über HasUniqueSlug (generateUniqueSlug +
  saveWithUniqueSlugRetry), redundanter Controller-Helper entfernt (M3).
- API-/Legacy-Text wird vor der Persistenz sanitisiert (M6);
  Scheduler/Legacy-Tracking ignorieren soft-gelöschte PMs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-14 13:47:51 +00:00
parent 7043ea8ad5
commit 0b35c3b04d
19 changed files with 536 additions and 91 deletions

View file

@ -192,6 +192,23 @@ test('Command ignoriert PMs ohne scheduled_at', function () {
expect($manual->fresh()->status)->toBe(PressReleaseStatus::Review);
});
test('Command ignoriert gelöschte fällige Pressemitteilungen', function () {
Carbon::setTestNow('2026-06-01 12:00:00');
$deleted = PressRelease::factory()->create([
'status' => PressReleaseStatus::Review->value,
'classification' => PressReleaseClassification::Green->value,
'scheduled_at' => '2026-06-01 11:55:00',
'published_at' => null,
]);
$deleted->delete();
Artisan::call(PublishScheduledPressReleases::class);
expect(PressRelease::withTrashed()->findOrFail($deleted->id)->status)
->toBe(PressReleaseStatus::Review);
});
test('Command läuft mit dry-run ohne Statusänderung', function () {
/** @var TestCase $this */
Carbon::setTestNow('2026-06-01 12:00:00');