seed(RolesAndPermissionsSeeder::class); $this->wallet = app(CreditWalletService::class); $this->proof = app(ProofPdfService::class); $this->extraPm = app(ExtraPmPurchaseService::class); }); function customerWithRole(): User { $user = User::factory()->create(['is_active' => true, 'email_verified_at' => now()]); $user->assignRole('customer'); return $user; } // ---------------------------------------------------------------- Proof-PDF test('buying a proof debits three credits and records a paid purchase', function () { $user = customerWithRole(); $this->wallet->credit($user, 10); $pr = PressRelease::factory()->published()->create(['user_id' => $user->id]); $purchase = $this->proof->purchase($user, $pr); expect($purchase->type)->toBe(SinglePurchaseType::ProofPdf); expect($purchase->status)->toBe(SinglePurchaseStatus::Paid); expect($this->wallet->balance($user))->toBe(7); expect($this->proof->hasPurchased($user, $pr))->toBeTrue(); }); test('re-buying the same proof does not charge again', function () { $user = customerWithRole(); $this->wallet->credit($user, 10); $pr = PressRelease::factory()->published()->create(['user_id' => $user->id]); $first = $this->proof->purchase($user, $pr); $second = $this->proof->purchase($user, $pr); expect($second->id)->toBe($first->id); expect($this->wallet->balance($user))->toBe(7); expect($user->singlePurchases()->where('type', SinglePurchaseType::ProofPdf->value)->count())->toBe(1); }); test('a proof for an unpublished release is rejected', function () { $user = customerWithRole(); $this->wallet->credit($user, 10); $draft = PressRelease::factory()->create(['user_id' => $user->id, 'status' => PressReleaseStatus::Draft->value]); expect(fn () => $this->proof->purchase($user, $draft)) ->toThrow(ProofPdfNotAvailableException::class); expect($this->wallet->balance($user))->toBe(10); }); test('a proof without enough credits throws and records nothing', function () { $user = customerWithRole(); $this->wallet->credit($user, 2); $pr = PressRelease::factory()->published()->create(['user_id' => $user->id]); expect(fn () => $this->proof->purchase($user, $pr)) ->toThrow(InsufficientCreditsException::class); expect($user->singlePurchases()->count())->toBe(0); }); test('the rendered proof is a non-trivial pdf document', function () { $pr = PressRelease::factory()->published()->create(['title' => 'Testmeldung', 'text' => str_repeat('Inhalt. ', 50)]); $pdf = app(ProofPdfRenderer::class)->render($pr); expect($pdf)->toStartWith('%PDF-1.4'); expect($pdf)->toContain('%%EOF'); expect(strlen($pdf))->toBeGreaterThan(800); }); test('the proof download requires a prior purchase', function () { $user = customerWithRole(); $pr = PressRelease::factory()->published()->create(['user_id' => $user->id]); // Ohne Kauf: 403. $this->actingAs($user) ->get(route('me.press-releases.proof', $pr->id)) ->assertForbidden(); // Nach Kauf: PDF-Download. $this->wallet->credit($user, 5); $this->proof->purchase($user, $pr); $this->actingAs($user) ->get(route('me.press-releases.proof', $pr->id)) ->assertOk() ->assertHeader('content-type', 'application/pdf'); }); // ---------------------------------------------------------------- Extra-PM test('an Einzel user pays the full extra-pm rate from the wallet', function () { $user = customerWithRole(); $this->wallet->credit($user, 25); $purchase = $this->extraPm->purchase($user); expect($purchase->type)->toBe(SinglePurchaseType::ExtraPm); expect($purchase->status)->toBe(SinglePurchaseStatus::Paid); expect($purchase->price_cents)->toBe(1900); expect($this->wallet->balance($user))->toBe(6); }); test('an extra-pm purchase extends the press release quota', function () { config()->set('billing.enforce_booking', true); $user = customerWithRole(); $plan = Plan::factory()->create([ 'slug' => 'business', 'press_release_quota' => 3, 'stripe_price_id_monthly' => 'price_biz_'.fake()->unique()->randomNumber(6), ]); subscribeUserToPlan($user, $plan); $user->update(['press_release_quota_used_this_month' => 3]); // Kontingent voll $this->wallet->credit($user, 20); expect($user->pressReleaseQuotaRemaining())->toBe(0); // Business-Tier zahlt 12 Credits für die Extra-PM. $this->extraPm->purchase($user); expect($this->wallet->balance($user))->toBe(8); expect($user->fresh()->pressReleaseQuotaRemaining())->toBe(1); }); test('an extra-pm without enough credits throws the mini-checkout signal', function () { $user = customerWithRole(); $this->wallet->credit($user, 8); try { $this->extraPm->purchase($user); $this->fail('Expected InsufficientCreditsException.'); } catch (InsufficientCreditsException $e) { expect($e->required)->toBe(19); expect($e->available)->toBe(8); expect($e->shortfall())->toBe(11); } expect($user->singlePurchases()->count())->toBe(0); });