customer = Customer::factory()->create(['total' => 10_000_000]); $this->product = Product::factory()->create([ 'product_type' => 'vps', 'is_active' => true, 'cpu' => 2, 'ram' => 2048, 'disk' => 40, ]); $this->cpuAddon = Addon::create([ 'type' => 'vps', 'code' => 'cpu', 'name' => 'CPU', 'min' => 1, 'max' => 8, 'step' => 1, 'price' => 50_000, 'status' => true, ]); $order = Order::create([ 'order_num' => 'ORD-UPG-TEST', 'customer_id' => $this->customer->id, 'total' => 100_000, 'status' => 1, ]); $this->orderItem = OrderItem::create([ 'order_id' => $order->id, 'product_id' => $this->product->id, 'billing_cycle' => 'monthly', 'months' => 1, 'quantity' => 1, 'price' => 100_000, 'addons_meta' => [], ]); $this->vm = ModuleVmware::create([ 'name' => 'upgrade-test-vm', 'customer_id' => $this->customer->id, 'order_id' => $order->id, 'vmware_id' => 'vm-upgrade-1', 'status' => 1, ]); $this->service = Service::create([ 'customer_id' => $this->customer->id, 'product_id' => $this->product->id, 'order_item_id' => $this->orderItem->id, 'vm_id' => $this->vm->id, 'status' => 'active', 'started_at' => now(), 'expires_at' => now()->addDays(15), ]); } private function actingAsCustomer(): static { return $this->actingAs($this->customer, 'client') ->withHeaders(['x-access-token' => $this->buildClientAccessToken()]); } public function test_upgrade_request_creates_unpaid_invoice_without_touching_vcenter(): void { Bus::fake(); $response = $this->actingAsCustomer()->postJson('/v1/system/vm-upgrade-addons', [ 'product' => $this->vm->id, 'cpu' => 2, ]); $response->assertStatus(200)->assertJsonPath('error', false); $this->assertDatabaseHas('invoices', [ 'customer_id' => $this->customer->id, 'service_id' => $this->service->id, 'invoice_type' => 3, 'status' => InvoiceStatus::UNPAID->value, ]); $this->assertDatabaseHas('invoice_items', [ 'addon_id' => $this->cpuAddon->id, 'type' => 'upgrade', 'quantity' => 2, ]); Bus::assertNotDispatched(UpdateVmConfigJob::class); } public function test_qty_over_max_is_rejected(): void { $response = $this->actingAsCustomer()->postJson('/v1/system/vm-upgrade-addons', [ 'product' => $this->vm->id, 'cpu' => 999, ]); $response->assertStatus(422)->assertJsonPath('error', true); $this->assertDatabaseCount('invoices', 0); } public function test_paying_the_invoice_dispatches_upgrade_job_and_updates_addons_meta(): void { $this->actingAsCustomer()->postJson('/v1/system/vm-upgrade-addons', [ 'product' => $this->vm->id, 'cpu' => 2, ])->assertStatus(200); $invoice = Invoice::where('service_id', $this->service->id)->firstOrFail(); Bus::fake(); app(InvoicePaymentService::class)->payWithBalance($invoice, $this->customer->fresh(), 'balance'); Bus::assertDispatched(UpdateVmConfigJob::class, function (UpdateVmConfigJob $job) { return $job->moduleVmwareId === $this->vm->id && $job->cpuOverride === 4; // base 2 + purchased 2 }); $meta = $this->orderItem->fresh()->addons_meta; $this->assertNotEmpty($meta); $this->assertSame(2, (int) collect($meta)->firstWhere('addon_id', $this->cpuAddon->id)['qty']); } }