seed(RolesAndPermissionsSeeder::class); }); test('submit for review logs a status change with customer source', function () { /** @var TestCase $this */ $customer = User::factory()->create(['is_active' => true]); $customer->assignRole('customer'); $pr = PressRelease::factory()->create([ 'user_id' => $customer->id, 'status' => PressReleaseStatus::Draft->value, ]); $this->actingAs($customer); app(PressReleaseService::class)->submitForReview($pr); expect($pr->fresh()->status)->toBe(PressReleaseStatus::Review); $log = PressReleaseStatusLog::query()->latest('id')->firstOrFail(); expect($log->press_release_id)->toBe($pr->id); expect($log->from_status)->toBe(PressReleaseStatus::Draft); expect($log->to_status)->toBe(PressReleaseStatus::Review); expect($log->source)->toBe('customer'); expect($log->changed_by_user_id)->toBe($customer->id); }); test('admin can reject a review press release with a reason', function () { /** @var TestCase $this */ Mail::fake(); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $author = User::factory()->create(['is_active' => true]); $author->assignRole('customer'); $pr = PressRelease::factory()->create([ 'user_id' => $author->id, 'status' => PressReleaseStatus::Review->value, ]); $this->actingAs($admin); LivewireVolt::test('admin.press-releases.show', ['id' => $pr->id]) ->set('rejectReason', 'Bitte überarbeiten Sie den werblichen Sprachstil.') ->call('reject') ->assertHasNoErrors(); $pr->refresh(); expect($pr->status)->toBe(PressReleaseStatus::Rejected); $log = PressReleaseStatusLog::query() ->where('press_release_id', $pr->id) ->latest('id') ->firstOrFail(); expect($log->to_status)->toBe(PressReleaseStatus::Rejected); expect($log->reason)->toBe('Bitte überarbeiten Sie den werblichen Sprachstil.'); expect($log->changed_by_user_id)->toBe($admin->id); Mail::assertQueued(PressReleaseRejected::class, function (PressReleaseRejected $mail) use ($author) { return $mail->user->is($author) && $mail->reason === 'Bitte überarbeiten Sie den werblichen Sprachstil.'; }); }); test('admin reject without reason fails validation', function () { /** @var TestCase $this */ $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $pr = PressRelease::factory()->create([ 'status' => PressReleaseStatus::Review->value, ]); $this->actingAs($admin); LivewireVolt::test('admin.press-releases.show', ['id' => $pr->id]) ->set('rejectReason', '') ->call('reject') ->assertHasErrors(['rejectReason']); expect($pr->fresh()->status)->toBe(PressReleaseStatus::Review); }); test('admin publish notifies customer with me edit url', function () { /** @var TestCase $this */ Mail::fake(); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $author = User::factory()->create(['is_active' => true]); $author->assignRole('customer'); $pr = PressRelease::factory()->create([ 'user_id' => $author->id, 'status' => PressReleaseStatus::Review->value, ]); $this->actingAs($admin); app(PressReleaseService::class)->publish($pr); Mail::assertQueued(PressReleasePublished::class, function (PressReleasePublished $mail) use ($pr) { return str_contains($mail->showUrl, '/admin/me/press-releases/'.$pr->id); }); }); test('rejected mail uses customer edit url for customer recipients', function () { /** @var TestCase $this */ Mail::fake(); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $author = User::factory()->create(['is_active' => true]); $author->assignRole('customer'); $pr = PressRelease::factory()->create([ 'user_id' => $author->id, 'status' => PressReleaseStatus::Review->value, ]); $this->actingAs($admin); app(PressReleaseService::class)->reject($pr, 'Test-Grund'); Mail::assertQueued(PressReleaseRejected::class, function (PressReleaseRejected $mail) use ($pr) { return str_contains($mail->editUrl, '/admin/me/press-releases/'.$pr->id.'/edit'); }); }); test('admin can resubmit cycle creates two audit log entries', function () { /** @var TestCase $this */ $author = User::factory()->create(['is_active' => true]); $author->assignRole('customer'); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $pr = PressRelease::factory()->create([ 'user_id' => $author->id, 'status' => PressReleaseStatus::Draft->value, ]); $this->actingAs($author); app(PressReleaseService::class)->submitForReview($pr); $this->actingAs($admin); app(PressReleaseService::class)->reject($pr->fresh(), 'Bitte korrigieren'); $this->actingAs($author); app(PressReleaseService::class)->submitForReview($pr->fresh()); $logs = PressReleaseStatusLog::query() ->where('press_release_id', $pr->id) ->orderBy('id') ->get(); expect($logs)->toHaveCount(3); expect($logs[0]->to_status)->toBe(PressReleaseStatus::Review); expect($logs[1]->to_status)->toBe(PressReleaseStatus::Rejected); expect($logs[2]->to_status)->toBe(PressReleaseStatus::Review); }); test('customer show page surfaces latest reject reason prominently', function () { /** @var TestCase $this */ $customer = User::factory()->create(['is_active' => true]); $customer->assignRole('customer'); $pr = PressRelease::factory()->create([ 'user_id' => $customer->id, 'status' => PressReleaseStatus::Rejected->value, ]); PressReleaseStatusLog::query()->create([ 'press_release_id' => $pr->id, 'changed_by_user_id' => null, 'from_status' => PressReleaseStatus::Review->value, 'to_status' => PressReleaseStatus::Rejected->value, 'reason' => 'Werbliche Sprache wurde markiert.', 'source' => 'admin', 'created_at' => now(), ]); $this->actingAs($customer); LivewireVolt::test('customer.press-releases.show', ['id' => $pr->id]) ->assertSee('Werbliche Sprache wurde markiert.') ->assertSee('Erneut einreichen'); }); test('admin index publish goes through service and creates audit log', function () { /** @var TestCase $this */ Mail::fake(); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $pr = PressRelease::factory()->create([ 'status' => PressReleaseStatus::Review->value, ]); $this->actingAs($admin); LivewireVolt::test('admin.press-releases.index') ->call('publish', $pr->id) ->assertHasNoErrors(); expect($pr->fresh()->status)->toBe(PressReleaseStatus::Published); $log = PressReleaseStatusLog::query() ->where('press_release_id', $pr->id) ->where('to_status', PressReleaseStatus::Published->value) ->firstOrFail(); expect($log->changed_by_user_id)->toBe($admin->id); }); test('admin index reject from list creates audit log with default reason', function () { /** @var TestCase $this */ Mail::fake(); $admin = User::factory()->create(['is_active' => true]); $admin->assignRole('admin'); $pr = PressRelease::factory()->create([ 'status' => PressReleaseStatus::Review->value, ]); $this->actingAs($admin); LivewireVolt::test('admin.press-releases.index') ->call('reject', $pr->id); expect($pr->fresh()->status)->toBe(PressReleaseStatus::Rejected); $log = PressReleaseStatusLog::query() ->where('press_release_id', $pr->id) ->latest('id') ->firstOrFail(); expect($log->reason)->not->toBeNull(); }); test('review counter helper returns correct number', function () { /** @var TestCase $this */ PressRelease::factory()->count(3)->create(['status' => PressReleaseStatus::Review->value]); PressRelease::factory()->count(2)->create(['status' => PressReleaseStatus::Draft->value]); PressRelease::factory()->count(1)->create(['status' => PressReleaseStatus::Published->value]); $count = app(AdminPerformanceCache::class)->pressReleaseReviewCount(); expect($count)->toBe(3); });