CluPilotCloud/tests/Feature/Provisioning/ServicesTest.php

106 lines
4.0 KiB
PHP

<?php
use App\Models\Host;
use App\Services\Proxmox\FakeProxmoxClient;
use App\Services\Ssh\CommandResult;
use App\Services\Ssh\FakeRemoteShell;
use App\Services\Wireguard\FakeWireguardHub;
use App\Services\Wireguard\LocalWireguardHub;
it('scripts remote command output and records calls (FakeRemoteShell)', function () {
$shell = new FakeRemoteShell;
$shell->script('dpkg -l proxmox-ve', CommandResult::success('ii proxmox-ve 8.2'));
$shell->connectWithPassword('1.2.3.4', 'root', 'pw');
expect($shell->run('dpkg -l proxmox-ve')->stdout)->toContain('proxmox-ve')
->and($shell->run('whoami')->ok())->toBeTrue() // default success
->and($shell->ran('dpkg -l proxmox-ve'))->toBeTrue();
$shell->putFile('/etc/wireguard/wg0.conf', '[Interface]');
expect($shell->files()['/etc/wireguard/wg0.conf'])->toBe('[Interface]')
->and($shell->connectionsWith('password'))->toHaveCount(1);
});
it('removes a peer via the RemoveWireguardPeer job', function () {
$hub = new FakeWireguardHub;
$hub->addPeer('PK', '10.66.0.9');
(new App\Provisioning\Jobs\RemoveWireguardPeer('PK'))->handle($hub);
expect($hub->peers())->toBe([]);
});
it('provides a deterministic VM lifecycle (FakeProxmoxClient)', function () {
$pve = new FakeProxmoxClient;
$a = $pve->nextVmid();
$b = $pve->nextVmid();
expect($b)->toBe($a + 1);
$upid = $pve->cloneVm('pve', 9000, $a, 'nc-test');
expect($upid)->toContain((string) $a)
->and($pve->clonedVmids)->toContain($a)
->and($pve->taskStatus('pve', $upid))->toBe(['status' => 'stopped', 'exitstatus' => 'OK'])
->and($pve->vmStatus('pve', $a)['status'])->toBe('stopped');
$pve->startVm('pve', $a);
expect($pve->vmStatus('pve', $a)['status'])->toBe('running')
->and($pve->guestAgentPing('pve', $a))->toBeTrue();
$pve->guestScript('occ status', 0, 'installed: true');
expect($pve->guestExec('pve', $a, 'occ status')['out-data'])->toContain('installed')
->and($pve->guestExec('pve', $a, 'whoami')['exitcode'])->toBe(0)
->and($pve->guestRan('occ status'))->toBeTrue();
});
it('can force a running task and a failed exit status (FakeProxmoxClient)', function () {
$pve = new FakeProxmoxClient;
$pve->forceTaskStatus = 'running';
expect($pve->taskStatus('pve', 'UPID')['status'])->toBe('running');
$pve->forceTaskStatus = null;
$pve->taskExitStatus = 'clone failed';
expect($pve->taskStatus('pve', 'UPID')['exitstatus'])->toBe('clone failed');
});
it('allocates ips and tracks peers (FakeWireguardHub)', function () {
$hub = new FakeWireguardHub;
$a = $hub->allocateIp();
$b = $hub->allocateIp();
expect($a)->not->toBe($b);
$hub->addPeer('PUBKEY', '10.66.0.9');
expect($hub->peers())->toBe(['PUBKEY' => '10.66.0.9']);
$hub->removePeer('PUBKEY');
expect($hub->peers())->toBe([]);
});
it('allocates addresses within a /24 subnet, skipping hub and used (LocalWireguardHub)', function () {
config()->set('provisioning.wireguard.subnet', '10.66.0.0/24');
config()->set('provisioning.wireguard.hub_ip', '10.66.0.1');
Host::factory()->create(['wg_ip' => '10.66.0.2']);
// .0 network, .1 hub, .2 used → first free is .3
expect((new LocalWireguardHub)->allocateIp())->toBe('10.66.0.3');
});
it('respects a non-/24 subnet (LocalWireguardHub)', function () {
config()->set('provisioning.wireguard.subnet', '10.66.5.128/25');
config()->set('provisioning.wireguard.hub_ip', '10.66.5.129');
// network .128, hub .129 → first free is .130
expect((new LocalWireguardHub)->allocateIp())->toBe('10.66.5.130');
});
it('reports node capacity for a host (FakeProxmoxClient)', function () {
$host = Host::factory()->create(['wg_ip' => '10.66.0.5']);
$client = (new FakeProxmoxClient)->forHost($host);
expect($client->listNodes())->not->toBeEmpty()
->and($client->nodeStatus('pve')['memory']['total'])->toBeGreaterThan(0)
->and($client->nodeStorage('pve')[0]['total'])->toBeGreaterThan(0);
});