service = app(ReviewCheckService::class); $this->wallet = app(CreditWalletService::class); }); test('an Einzel user has four free checks per month', function () { $user = User::factory()->create(); expect($this->service->freeRemaining($user))->toBe(4); expect($this->service->nextCheckCosts($user))->toBeFalse(); }); test('free checks are consumed from the monthly quota first', function () { $user = User::factory()->create(); $check = $this->service->recordCheck($user); expect($check->source)->toBe(ReviewCheckSource::Free); expect($check->charged_credits)->toBe(0); expect($this->service->freeRemaining($user))->toBe(3); expect($this->service->usedThisMonth($user))->toBe(1); }); test('once the free quota is empty the overflow draws one credit per check', function () { $user = User::factory()->create(); $this->wallet->credit($user, 5); // Vier freie Prüfungen aufbrauchen. for ($i = 0; $i < 4; $i++) { $this->service->recordCheck($user); } expect($this->service->freeRemaining($user))->toBe(0); expect($this->service->nextCheckCosts($user))->toBeTrue(); $overflow = $this->service->recordCheck($user); expect($overflow->source)->toBe(ReviewCheckSource::Credit); expect($overflow->charged_credits)->toBe(1); expect($this->wallet->balance($user))->toBe(4); }); test('overflow without enough credits throws and records nothing', function () { $user = User::factory()->create(); for ($i = 0; $i < 4; $i++) { $this->service->recordCheck($user); } expect(fn () => $this->service->recordCheck($user)) ->toThrow(InsufficientCreditsException::class); expect($this->service->usedThisMonth($user))->toBe(4); // kein Overflow-Eintrag }); test('the daily limit is a hard brake that credits cannot bypass', function () { $user = User::factory()->create(); $this->wallet->credit($user, 100); // 10 Prüfungen heute (Tageslimit) – 4 frei, 6 per Credit. for ($i = 0; $i < 10; $i++) { $this->service->recordCheck($user); } expect($this->service->dailyLimitReached($user))->toBeTrue(); expect($this->service->canCheck($user))->toBeFalse(); expect(fn () => $this->service->recordCheck($user)) ->toThrow(ReviewLimitException::class); }); test('a higher tier check pool resets with the calendar month', function () { $user = User::factory()->create(); // Letzten Monat verbrauchte Prüfungen zählen nicht ins aktuelle Kontingent. ReviewCheckFactoryHelper($user, 3, now()->subMonth()); expect($this->service->usedThisMonth($user))->toBe(0); expect($this->service->freeRemaining($user))->toBe(4); }); /** * Legt Prüfungen mit explizitem Datum an (umgeht den Service, der immer * „jetzt" bucht). */ function ReviewCheckFactoryHelper(User $user, int $count, $at): void { for ($i = 0; $i < $count; $i++) { $check = $user->reviewChecks()->create([ 'source' => ReviewCheckSource::Free, 'charged_credits' => 0, ]); $check->forceFill(['created_at' => $at, 'updated_at' => $at])->save(); } }