stripe = new FakeStripeClient; app()->instance(StripeClient::class, $this->stripe); // Der Bestand, gegen den hier geprüft wird: die Familie ist da, sie ist // intern, und sie steht im Verkauf — denn `sales_enabled` schaltet auch das // Verschenken ab, und genau das soll `internal` nicht tun. $family = PlanFamily::query()->where('key', 'test')->sole(); expect($family->internal)->toBeTrue() ->and($family->sales_enabled)->toBeTrue(); }); it('zeigt ein internes Paket nicht im Verkauf', function () { expect(array_keys(app(PlanCatalogue::class)->sellable()))->not->toContain('test'); }); it('lässt ein internes Paket weiterhin verschenken', function () { // currentVersion() ist der Weg, den GrantPlan benutzt — wirft er hier, // ist das Verschenken mit abgeschaltet, und genau das soll `internal` // gerade NICHT tun. expect(app(PlanCatalogue::class)->currentVersion('test')->quota_gb)->toBe(5); }); it('lehnt einen Checkout auf ein internes Paket ab, nicht unterscheidbar von einem unbekannten', function () { $buyer = User::factory()->create(['email_verified_at' => now()]); // Eine URL ist keine Liste: dass "test" in keinem Preisblatt steht, hält // niemanden davon ab, den Schlüssel direkt zu posten. Erwartet wird // dieselbe Antwort wie für einen Schlüssel, den es gar nicht gibt — wer // die Preisliste nicht kennt, soll aus der Antwort nicht lernen können, // dass es dieses Paket gibt. $this->actingAs($buyer) ->post(route('checkout.start'), ['plan' => 'test', 'terms_accepted' => '1']) ->assertSessionHasErrors(['plan' => __('checkout.plan_gone')]); $this->actingAs($buyer) ->post(route('checkout.start'), ['plan' => 'gibt-es-nicht', 'terms_accepted' => '1']) ->assertSessionHasErrors(['plan' => __('checkout.plan_gone')]); expect($this->stripe->checkouts)->toBeEmpty(); });