From 3ed80a05e086950feca5217ccbaf6b19ccd1ff85 Mon Sep 17 00:00:00 2001 From: nexxo Date: Sat, 1 Aug 2026 14:31:52 +0200 Subject: [PATCH] Ein Block bringt seinen eigenen Kopfraum mit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ein Block gibt 20 GB und belegt 22. Ohne das haette ein Start mit drei Bloecken 90 GB auf 100 GB Platte gehabt — zehn Gigabyte Kopfraum, wo die Regel bei dieser Plattengroesse zwoelf verlangt, und der gestapelte Tarif waere genau der Fall geworden, den die Regel verhindern soll. Co-Authored-By: Claude Opus 5 --- .../Steps/Customer/ResizeVirtualMachine.php | 13 +++- .../Provisioning/StoragePackHeadroomTest.php | 66 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 tests/Feature/Provisioning/StoragePackHeadroomTest.php diff --git a/app/Provisioning/Steps/Customer/ResizeVirtualMachine.php b/app/Provisioning/Steps/Customer/ResizeVirtualMachine.php index e2a930c..d1b582a 100644 --- a/app/Provisioning/Steps/Customer/ResizeVirtualMachine.php +++ b/app/Provisioning/Steps/Customer/ResizeVirtualMachine.php @@ -111,8 +111,19 @@ class ResizeVirtualMachine extends CustomerStep $allowance = StorageAllowance::for($instance); + // Kontingent + Blöcke + Kopfraum des Pakets + Kopfraum der Blöcke. + // + // Der letzte Summand ist der neue: ein Block gibt dem Kunden 20 GB und + // belegt 22, damit die Regel max(10 GB, 12 %) auch für ein gestapeltes + // Paket gilt. Ohne ihn hätte ein Start mit drei Blöcken 90 GB auf 100 GB + // Platte — zehn Gigabyte Kopfraum, wo die Regel zwölf verlangt. + // + // Beide Kopfräume werden gelesen, nicht abgeleitet: der des Pakets aus + // dem eingefrorenen Vertrag, der der Blöcke aus den Buchungen. $target = $allowance->totalGb() > 0 - ? $allowance->totalGb() + max(0, $planDisk - $planQuota) + ? $allowance->totalGb() + + max(0, $planDisk - $planQuota) + + max(0, $allowance->packDiskGb() - $allowance->packGb()) : $planDisk; $actual = (int) $instance->disk_gb; diff --git a/tests/Feature/Provisioning/StoragePackHeadroomTest.php b/tests/Feature/Provisioning/StoragePackHeadroomTest.php new file mode 100644 index 0000000..cea8fa1 --- /dev/null +++ b/tests/Feature/Provisioning/StoragePackHeadroomTest.php @@ -0,0 +1,66 @@ +totalGb() + + max(0, $planDisk - $planQuota) + + ($allowance->packDiskGb() - $allowance->packGb()); + + expect($target)->toBe(106); +}); + +// Den Schritt selbst fahren: reservedRun() (aus CustomerStepsTest.php) baut ein +// 'start'-Paket (100 GB Kontingent, 120 GB Platte — 20 GB Kopfraum des Pakets). +// Drei Blöcke zu 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] = reservedRun(); + + 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); +});