guestScript('status.php', 0, 'ok'); $s['pve']->guestScript('hostname -I', 0, '10.20.0.9'); // deploy health + acceptance probe // Fake defaults: tasks stopped/OK, guest agent up, cert reachable. Host::factory()->active()->create(['datacenter' => 'fsn', 'node' => 'pve', 'total_gb' => 1000]); $order = Order::factory()->create(['status' => 'paid', 'plan' => 'start', 'datacenter' => 'fsn']); $run = ProvisioningRun::factory()->create([ 'subject_type' => Order::class, 'subject_id' => $order->id, 'pipeline' => 'customer', 'context' => [], ]); $runner = app(RunRunner::class); for ($i = 0; $i < 60; $i++) { $run->refresh(); if (in_array($run->status, ['completed', 'failed'], true)) { break; } if ($run->next_attempt_at?->isFuture()) { $run->update(['next_attempt_at' => now()]); } $runner->advance($run); } $run->refresh(); $instance = Instance::query()->where('order_id', $order->id)->firstOrFail(); expect($run->status)->toBe('completed') ->and($run->error)->toBeNull() ->and($order->fresh()->status)->toBe('active') ->and($instance->status)->toBe('active') ->and($instance->vmid)->not->toBeNull() ->and($instance->cert_ok)->toBeTrue() ->and($instance->onboardingTasks()->count())->toBeGreaterThan(0); // Every external resource created exactly once. foreach (['instance_id', 'vmid', 'dns_record_id', 'nc_admin', 'backup_job_id', 'monitoring_target_id'] as $kind) { expect(RunResource::where('run_id', $run->id)->where('kind', $kind)->count())->toBe(1); } // Secrets scrubbed from state; credentials delivered. expect($run->context('admin_password'))->toBeNull() ->and($run->context('db_password'))->toBeNull(); Notification::assertSentOnDemand(CloudReady::class); }); it('does not duplicate external resources when a step re-runs after a crash', function () { Notification::fake(); Queue::fake(); $s = fakeServices(); $s['pve']->guestScript('status.php', 0, 'ok'); $s['pve']->guestScript('hostname -I', 0, '10.20.0.9'); Host::factory()->active()->create(['datacenter' => 'fsn', 'node' => 'pve', 'total_gb' => 1000]); $order = Order::factory()->create(['status' => 'paid', 'plan' => 'start', 'datacenter' => 'fsn']); $run = ProvisioningRun::factory()->create([ 'subject_type' => Order::class, 'subject_id' => $order->id, 'pipeline' => 'customer', 'context' => [], ]); $runner = app(RunRunner::class); // Drive a few steps, then rewind to the clone step and replay it. for ($i = 0; $i < 5; $i++) { $run->refresh(); if ($run->next_attempt_at?->isFuture()) { $run->update(['next_attempt_at' => now()]); } $runner->advance($run); } $cloneIndex = array_search( \App\Provisioning\Steps\Customer\CloneVirtualMachine::class, config('provisioning.pipelines.customer'), true, ); $run->update(['current_step' => $cloneIndex, 'status' => 'running']); $runner->advance($run->fresh()); expect(RunResource::where('run_id', $run->id)->where('kind', 'vmid')->count())->toBe(1); });