seed(RolesAndPermissionsSeeder::class); }); function quotaTestPressRelease(User $user, string $status = 'draft'): PressRelease { $company = Company::factory()->presseecho()->create(); $user->companies()->attach($company->id, ['role' => 'owner']); return PressRelease::factory()->create([ 'user_id' => $user->id, 'company_id' => $company->id, 'category_id' => Category::factory()->create()->id, 'portal' => $company->portal->value, 'status' => $status, ]); } test('remaining quota reflects the used counter', function () { $user = User::factory()->create([ 'press_release_quota' => 3, 'press_release_quota_used_this_month' => 1, ]); expect($user->pressReleaseQuotaRemaining())->toBe(2); }); test('submitting a press release does not consume a quota slot', function () { // Decision-Update §3.2: Der Slot zählt erst bei Veröffentlichung runter. Queue::fake(); $user = User::factory()->create([ 'press_release_quota' => 3, 'press_release_quota_used_this_month' => 0, ]); $user->assignRole('customer'); $pr = quotaTestPressRelease($user); app(PressReleaseService::class)->submitForReview($pr); expect($pr->fresh()->status)->toBe(PressReleaseStatus::Review); expect($user->fresh()->press_release_quota_used_this_month)->toBe(0); }); test('publishing consumes exactly one quota slot', function () { $user = User::factory()->create([ 'press_release_quota' => 3, 'press_release_quota_used_this_month' => 0, ]); $user->assignRole('customer'); $pr = quotaTestPressRelease($user, 'review'); app(PressReleaseService::class)->publish($pr); expect($pr->fresh()->status)->toBe(PressReleaseStatus::Published); expect($user->fresh()->press_release_quota_used_this_month)->toBe(1); }); test('re-publishing after archive does not consume a second slot', function () { $user = User::factory()->create([ 'press_release_quota' => 3, 'press_release_quota_used_this_month' => 0, ]); $user->assignRole('customer'); $pr = quotaTestPressRelease($user, 'review'); $service = app(PressReleaseService::class); $service->publish($pr); $service->archive($pr->fresh()); $service->changeStatusFromAdmin($pr->fresh(), PressReleaseStatus::Published); expect($user->fresh()->press_release_quota_used_this_month)->toBe(1); }); test('a rejected press release does not consume a quota slot', function () { $user = User::factory()->create([ 'press_release_quota' => 3, 'press_release_quota_used_this_month' => 0, ]); $user->assignRole('customer'); $pr = quotaTestPressRelease($user, 'review'); app(PressReleaseService::class)->reject($pr, 'Unzulässiger Inhalt.', 'ki'); expect($pr->fresh()->status)->toBe(PressReleaseStatus::Rejected); expect($user->fresh()->press_release_quota_used_this_month)->toBe(0); }); test('submitting with an exhausted quota is blocked', function () { Queue::fake(); $user = User::factory()->create([ 'press_release_quota' => 3, 'press_release_quota_used_this_month' => 3, ]); $user->assignRole('customer'); $pr = quotaTestPressRelease($user); expect(fn () => app(PressReleaseService::class)->submitForReview($pr)) ->toThrow(QuotaExceededException::class); expect($pr->fresh()->status)->toBe(PressReleaseStatus::Draft); }); test('monthly reset command zeroes the used counter', function () { User::factory()->count(2)->create(['press_release_quota_used_this_month' => 2]); $untouched = User::factory()->create(['press_release_quota_used_this_month' => 0]); $this->artisan(ResetMonthlyPressReleaseQuota::class)->assertSuccessful(); expect(User::where('press_release_quota_used_this_month', '>', 0)->count())->toBe(0); expect($untouched->fresh()->press_release_quota_used_this_month)->toBe(0); });