CluPilotCloud/tests/Feature/Provisioning/StoragePackHeadroomTest.php

94 lines
4.0 KiB
PHP

<?php
use App\Models\Host;
use App\Models\Instance;
use App\Models\Order;
use App\Models\ProvisioningRun;
use App\Models\SubscriptionAddon;
use App\Provisioning\Steps\Customer\ResizeVirtualMachine;
use App\Services\Billing\AddonCatalogue;
/**
* Was ein Block auf der Platte bedeutet.
*
* Der Kopfraum wächst mit der Instanz (max(10 GB, 12 %)), sonst gilt die Regel
* nur für frisch gekaufte Pakete und nicht für gestapelte. Eine volle Platte
* legt nicht den Upload still, sondern die Instanz.
*
* Absichtlich KEIN Test, der die Formel aus `growDisk()` isoliert nachrechnet:
* so eine Fassung wäre grün geblieben, selbst wenn der Schritt eine andere,
* falsche Formel benutzt hätte — und eine erste Fassung dieser Datei ist genau
* daran gescheitert (die nachgerechnete Zeile hatte kein `max(0, …)` um den
* neuen Summanden, der echte Code in `ResizeVirtualMachine.php` hat eins). Der
* einzige Test hier fährt den Schritt selbst und liest ab, was er tatsächlich
* tut.
*/
/**
* Ein frisch reservierter 'start'-Lauf: 100 GB Kontingent, 120 GB Platte
* (`database/migrations/2026_07_26_040000_create_plan_catalogue_tables.php`),
* kein Block gebucht.
*
* Absichtlich eine eigene, in sich geschlossene Hilfsfunktion statt
* `reservedRun()` aus `CustomerStepsTest.php` zu teilen: diese Datei muss auch
* ALLEIN laufen (`… test tests/Feature/Provisioning/StoragePackHeadroomTest.php`),
* nicht nur im Verbund mit einer Nachbardatei, die zufällig zuerst geladen
* wurde. Jede andere Datei in diesem Verzeichnis bringt ihre Hilfsfunktionen
* ebenso selbst mit (`hostRun()`, `restartableInstance()`,
* `paidOrderWithSubscription()`) — geteilt wird nur, was in `tests/Pest.php`
* steht, das Pest immer lädt.
*/
function packHeadroomRun(): array
{
$host = Host::factory()->active()->create(['datacenter' => 'fsn', 'node' => 'pve']);
$order = Order::factory()->withSubscription()->create(['datacenter' => 'fsn', 'plan' => 'start']);
$instance = Instance::factory()->create([
'order_id' => $order->id, 'customer_id' => $order->customer_id,
'host_id' => $host->id, 'vmid' => 101, 'status' => 'provisioning',
]);
$run = ProvisioningRun::factory()->create([
'subject_type' => Order::class, 'subject_id' => $order->id, 'pipeline' => 'customer',
'context' => [
'instance_id' => $instance->id, 'host_id' => $host->id, 'node' => 'pve',
'vmid' => 101, 'subdomain' => $instance->subdomain, 'plan' => 'start',
],
]);
return compact('host', 'order', 'instance', 'run');
}
// Drei Blöcke à 20 GB nutzbar / 22 GB belegt kommen direkt als
// SubscriptionAddon-Zeile dazu, statt über BookAddon zu buchen — das würde
// einen Lieferlauf anstoßen und Stripe befragen, was hier nichts zur Sache
// tut; gefragt ist nur, was ResizeVirtualMachine aus einer bereits gebuchten
// Zeile macht.
//
// Ziel: 100 (Kontingent) + 60 (drei Blöcke à 20) + 20 (Kopfraum des Pakets,
// 120-100) + 6 (Kopfraum der Blöcke, 66-60 belegt) = 186 GB.
it('vergrößert die Platte um Paket- und Block-Kopfraum, und nur einmal', function () {
$s = fakeServices();
['run' => $run, 'instance' => $instance, 'order' => $order] = packHeadroomRun();
SubscriptionAddon::create([
'subscription_id' => $order->subscription->id,
'addon_key' => AddonCatalogue::STORAGE,
'price_cents' => 1000,
'currency' => 'EUR',
'quantity' => 3,
'booked_at' => now(),
'pack_gb' => 20,
'pack_disk_gb' => 22,
]);
expect(app(ResizeVirtualMachine::class)->execute($run)->type)->toBe('advance')
->and($s['pve']->resizeCalls)->toContain('101:scsi0:186G')
->and($instance->fresh()->disk_gb)->toBe(186);
// Wiederholungslauf: das Ziel ist absolut, nicht '+…', also darf ein
// zweiter Durchlauf nicht noch einmal vergrößern.
$s['pve']->resizeCalls = [];
expect(app(ResizeVirtualMachine::class)->execute($run->fresh())->type)->toBe('advance')
->and($s['pve']->resizeCalls)->toBe([])
->and($instance->fresh()->disk_gb)->toBe(186);
});