CluPilotCloud/tests/Feature/Provisioning/CustomerProvisioningEndToEn...

95 lines
3.8 KiB
PHP

<?php
use App\Models\Host;
use App\Models\Instance;
use App\Models\Order;
use App\Models\ProvisioningRun;
use App\Models\RunResource;
use App\Notifications\CloudReady;
use App\Provisioning\RunRunner;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Queue;
it('provisions a paid order all the way to active (mocked)', function () {
Notification::fake();
Queue::fake(); // drive the runner manually, one step per advance
$s = fakeServices();
$s['pve']->guestScript('status.php', 0, 'ok');
$s['pve']->guestScript('hostname -I', 0, '10.20.0.9');
$s['pve']->guestScript('occ status', 0, '{"installed":true,"maintenance":false}'); // 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');
$s['pve']->guestScript('occ status', 0, '{"installed":true,"maintenance":false}');
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);
});