100 lines
4.1 KiB
PHP
100 lines
4.1 KiB
PHP
<?php
|
|
|
|
use App\Actions\BookAddon;
|
|
use App\Actions\GrantAddon;
|
|
use App\Actions\OpenSubscription;
|
|
use App\Livewire\Billing;
|
|
use App\Models\Customer;
|
|
use App\Models\Instance;
|
|
use App\Models\Operator;
|
|
use App\Models\Order;
|
|
use App\Models\User;
|
|
use App\Services\Billing\AddonCatalogue;
|
|
use App\Services\Billing\PlanCatalogue;
|
|
|
|
/**
|
|
* Was ein Block kostet, und wer ihn nicht kaufen kann.
|
|
*
|
|
* Die Preisregel: ein Block muss teurer je GB sein als der Aufstieg in die
|
|
* nächste Stufe. 20 GB für 15 € sind 0,75 €/GB gegen 0,73 (Start → Team) und
|
|
* 0,67 (Team → Business). Bei 12 € wäre er mit 0,60 billiger als jeder
|
|
* Aufstieg gewesen, und Stapeln hätte sich gelohnt.
|
|
*/
|
|
it('verkauft 20 GB für 15 Euro netto', function () {
|
|
$catalogue = app(AddonCatalogue::class);
|
|
|
|
expect($catalogue->packGb())->toBe(20)
|
|
->and($catalogue->packDiskGb())->toBe(22)
|
|
->and($catalogue->priceCents(AddonCatalogue::STORAGE))->toBe(1500)
|
|
->and($catalogue->maxQuantity(AddonCatalogue::STORAGE))->toBe(3);
|
|
});
|
|
|
|
it('bleibt teurer je Gigabyte als der Aufstieg', function () {
|
|
// Gerechnet wird mit dem, was Katalog und Konfiguration WIRKLICH sagen —
|
|
// mit festen Zahlen prüfte dieser Test nur, ob PHP dividieren kann. So
|
|
// schlägt er an, wenn ein Preis später einmal so gesetzt wird, dass
|
|
// Stapeln sich wieder lohnt.
|
|
$catalogue = app(AddonCatalogue::class);
|
|
$plans = app(PlanCatalogue::class)->sellable();
|
|
|
|
$blockPerGb = $catalogue->priceCents(AddonCatalogue::STORAGE) / $catalogue->packGb();
|
|
|
|
foreach ([['start', 'team'], ['team', 'business']] as [$von, $nach]) {
|
|
$mehrGb = $plans[$nach]['quota_gb'] - $plans[$von]['quota_gb'];
|
|
$mehrCent = $plans[$nach]['price_cents'] - $plans[$von]['price_cents'];
|
|
|
|
expect($blockPerGb)->toBeGreaterThan($mehrCent / $mehrGb);
|
|
}
|
|
});
|
|
|
|
it('bietet Enterprise keinen Zusatzspeicher an', function () {
|
|
$order = Order::factory()->create(['plan' => 'enterprise', 'datacenter' => 'fsn', 'status' => 'paid']);
|
|
$subscription = app(OpenSubscription::class)($order);
|
|
|
|
expect(fn () => app(BookAddon::class)($subscription, AddonCatalogue::STORAGE, 1))
|
|
->toThrow(RuntimeException::class);
|
|
});
|
|
|
|
/**
|
|
* Nachtrag: derselbe Ausschluss, wenn der Betreiber ein Modul verschenkt.
|
|
*
|
|
* GrantAddon bucht durch BookAddon (siehe dessen Kopfkommentar), also reicht
|
|
* die Prüfung an einer Stelle — dieser Test belegt nur, dass der Weg
|
|
* tatsächlich derselbe ist und nicht heimlich daran vorbeiführt.
|
|
*/
|
|
it('lässt auch den Betreiber keinen Zusatzspeicher an Enterprise verschenken', function () {
|
|
$order = Order::factory()->create(['plan' => 'enterprise', 'datacenter' => 'fsn', 'status' => 'paid']);
|
|
$subscription = app(OpenSubscription::class)($order);
|
|
$operator = Operator::factory()->create();
|
|
|
|
expect(fn () => app(GrantAddon::class)($subscription, $operator, AddonCatalogue::STORAGE, 0, 1))
|
|
->toThrow(RuntimeException::class);
|
|
});
|
|
|
|
/**
|
|
* Nachtrag: dieselbe Sperre, bevor Geld fließt, nicht erst danach.
|
|
*
|
|
* `storageLimitNote` ist das, was den Knopf im Portal verschwinden lässt (statt
|
|
* ihn anzubieten und dann an der Zahlung zu scheitern) — derselbe Grund, aus
|
|
* dem die kaufmännische Grenze dort schon geprüft wurde. Ohne diese Prüfung
|
|
* würde ein Enterprise-Kunde einen zahlbaren Auftrag in den Warenkorb legen
|
|
* können, den BookAddon nach der Zahlung nur noch ablehnen könnte, während der
|
|
* Auftrag selbst „paid" stehen bliebe.
|
|
*/
|
|
it('bietet Enterprise auch im Warenkorb keinen Zusatzspeicher an', function () {
|
|
$customer = Customer::factory()->create();
|
|
$user = User::factory()->create(['email' => $customer->email]);
|
|
$order = Order::factory()->withSubscription()->for($customer)->create(['plan' => 'enterprise']);
|
|
Instance::factory()->for($customer)->create([
|
|
'order_id' => $order->id, 'plan' => 'enterprise', 'status' => 'active',
|
|
]);
|
|
|
|
withStripeSecret();
|
|
|
|
Livewire\Livewire::actingAs($user)
|
|
->test(Billing::class)
|
|
->call('purchase', 'storage');
|
|
|
|
expect(Order::query()->where('customer_id', $customer->id)->where('type', 'storage')->count())->toBe(0);
|
|
});
|