CluPilotCloud/tests/Feature/Provisioning/HostOnboardingEndToEndTest.php

83 lines
3.0 KiB
PHP

<?php
use App\Actions\StartHostOnboarding;
use App\Models\ProvisioningRun;
use App\Models\RunResource;
use App\Provisioning\RunRunner;
use App\Services\Ssh\CommandResult;
use Illuminate\Support\Facades\Queue;
it('drives a fresh host all the way to active (mocked)', function () {
Queue::fake(); // drive the runner manually, one step per advance
$s = fakeServices();
$s['shell']->script('wg pubkey', CommandResult::success('HOSTPUBKEY0000='));
$s['shell']->script('uname -r', CommandResult::success('6.8.12-4-pve'));
$host = app(StartHostOnboarding::class)->run([
'name' => 'pve-fsn-9',
'datacenter' => 'fsn',
'public_ip' => '203.0.113.9',
'root_password' => 'rootpw',
]);
$run = ProvisioningRun::query()->where('subject_id', $host->id)->firstOrFail();
$runner = app(RunRunner::class);
for ($i = 0; $i < 40; $i++) {
$run->refresh();
if (in_array($run->status, ['completed', 'failed'], true)) {
break;
}
$runner->advance($run);
}
$run->refresh();
$host->refresh();
expect($run->status)->toBe('completed')
->and($run->error)->toBeNull()
->and($host->status)->toBe('active')
->and($host->wg_ip)->not->toBeNull()
->and($host->total_gb)->toBe(1024)
->and($host->cpu_cores)->toBe(16)
->and($host->api_token_ref)->toContain('automation@pve')
// external resources created exactly once
->and(RunResource::where('run_id', $run->id)->where('kind', 'wg_peer')->count())->toBe(1)
->and(RunResource::where('run_id', $run->id)->where('kind', 'pve_token')->count())->toBe(1)
// one-time root password scrubbed from state
->and($run->context('root_password'))->toBeNull();
});
it('does not duplicate external resources when a step re-runs after a crash', function () {
Queue::fake();
$s = fakeServices();
$s['shell']->script('wg pubkey', CommandResult::success('HOSTPUBKEY0000='));
$s['shell']->script('uname -r', CommandResult::success('6.8.12-4-pve'));
$host = app(StartHostOnboarding::class)->run([
'name' => 'pve-fsn-10',
'datacenter' => 'fsn',
'public_ip' => '203.0.113.10',
'root_password' => 'rootpw',
]);
$run = ProvisioningRun::query()->where('subject_id', $host->id)->firstOrFail();
$runner = app(RunRunner::class);
// Advance a few steps, then simulate a crash by rewinding the run to the
// WireGuard step and replaying it — the breadcrumb must prevent duplicates.
for ($i = 0; $i < 6; $i++) {
$run->refresh();
$runner->advance($run);
}
$run->refresh();
$wgIndex = array_search(
\App\Provisioning\Steps\Host\ConfigureWireguard::class,
config('provisioning.pipelines.host'),
true,
);
$run->update(['current_step' => $wgIndex, 'status' => 'running']);
$runner->advance($run->fresh());
expect(RunResource::where('run_id', $run->id)->where('kind', 'wg_peer')->count())->toBe(1);
});