create([ 'host_id' => App\Models\Host::factory()->active()->create()->id, 'vmid' => 1042, 'nc_admin_ref' => 'admin', 'subdomain' => 'kanzlei-berger', 'status' => 'active', ]); } it('is offered only to operators who may take over an installation', function () { $instance = adminAccessInstance(); foreach (['Support', 'Billing', 'Read-only', 'Developer'] as $role) { Livewire\Livewire::actingAs(operator($role), 'operator') ->test(InstanceAdminAccess::class, ['uuid' => $instance->uuid]) ->assertForbidden(); } Livewire\Livewire::actingAs(operator('Owner'), 'operator') ->test(InstanceAdminAccess::class, ['uuid' => $instance->uuid]) ->assertOk(); }); it('asks for the operator password before touching the instance', function () { Queue::fake(); $instance = adminAccessInstance(); $owner = operator('Owner'); $owner->forceFill(['password' => Hash::make('geheim-1234')])->save(); $modal = Livewire\Livewire::actingAs($owner, 'operator') ->test(InstanceAdminAccess::class, ['uuid' => $instance->uuid]) ->set('password', 'falsch') ->call('request') ->assertHasErrors('password'); Queue::assertNothingPushed(); $modal->set('password', 'geheim-1234')->call('request')->assertHasNoErrors(); Queue::assertPushed(IssueInstanceAdminAccess::class); }); it('resets our managed admin account and hands the credentials over once', function () { $pve = new FakeProxmoxClient; app()->instance(ProxmoxClient::class, $pve); $instance = adminAccessInstance(); $token = 'handoff-token-for-the-test'; (new IssueInstanceAdminAccess($instance->uuid, $token, 1))->handle($pve); $payload = json_decode(ConfigHandoff::get($token), true); expect($payload['username'])->toBe('admin') ->and($payload['password'])->toHaveLength(24) ->and($payload['url'])->toContain('kanzlei-berger'); // The password is handed over, never written to the instance record. expect($instance->fresh()->getAttributes())->not->toContain($payload['password']); }); it('reports a silent instance instead of pretending it worked', function () { $pve = new class extends FakeProxmoxClient { public function guestExec(string $node, int $vmid, string $command): array { return ['exitcode' => 1, 'out-data' => 'connection refused']; } }; app()->instance(ProxmoxClient::class, $pve); $instance = adminAccessInstance(); $token = 'handoff-failure'; (new IssueInstanceAdminAccess($instance->uuid, $token, 1))->handle($pve); expect(json_decode(ConfigHandoff::get($token), true))->toBe(['error' => 'failed']); }); it('refuses an instance that is no longer live', function () { $pve = new FakeProxmoxClient; app()->instance(ProxmoxClient::class, $pve); $instance = adminAccessInstance(); $instance->update(['status' => 'terminated']); (new IssueInstanceAdminAccess($instance->uuid, 'handoff-dead', 1))->handle($pve); // Its VMID may belong to someone else by now. expect(json_decode(ConfigHandoff::get('handoff-dead'), true))->toBe(['error' => 'unavailable']); }); it('tells the console when Proxmox is unreachable', function () { $pve = new class extends FakeProxmoxClient { public function guestExec(string $node, int $vmid, string $command): array { throw new RuntimeException('no route to host'); } }; app()->instance(ProxmoxClient::class, $pve); $instance = adminAccessInstance(); (new IssueInstanceAdminAccess($instance->uuid, 'handoff-throw', 1))->handle($pve); // Otherwise the modal polls forever with a spinner. expect(json_decode(ConfigHandoff::get('handoff-throw'), true))->toBe(['error' => 'failed']); }); it('shows the password once and not a second time', function () { $pve = new FakeProxmoxClient; app()->instance(ProxmoxClient::class, $pve); $instance = adminAccessInstance(); $owner = operator('Owner'); $owner->forceFill(['password' => Hash::make('geheim-1234')])->save(); $modal = Livewire\Livewire::actingAs($owner, 'operator') ->test(InstanceAdminAccess::class, ['uuid' => $instance->uuid]) ->set('password', 'geheim-1234') ->call('request'); // sync queue: the job runs immediately $token = $modal->get('token'); $modal->assertSee(__('instances.admin_ready')); // "Shown once" has to mean it: a replayed request must not fetch it again. expect(ConfigHandoff::get($token ?? 'none'))->toBeNull() ->and($modal->get('token'))->toBeNull(); $modal->call('$refresh')->assertDontSee(__('instances.admin_ready')); });