create(['must_change_password' => false]); } private function operator(): User { return User::factory()->operator()->create(['must_change_password' => false]); } private function viewer(): User { return User::factory()->viewer()->create(['must_change_password' => false]); } /** An online server with a credential, selected as the active fleet server. */ private function activeServer(): Server { $server = Server::create(['name' => 'box', 'ip' => '10.0.0.2', 'ssh_port' => 22, 'status' => 'online']); $server->credential()->create(['username' => 'root', 'auth_type' => 'password', 'secret' => 'x']); session(['active_server_id' => $server->id]); return $server; } // ── manage-fleet: Servers\Index add-server trigger ────────────────────────── public function test_admin_sees_the_add_server_trigger(): void { $this->actingAs($this->admin()); Livewire::test(Servers::class) ->assertOk() ->assertSee(__('servers.add_server')); // "Server hinzufügen" } public function test_viewer_does_not_see_the_add_server_trigger(): void { $this->actingAs($this->viewer()); Livewire::test(Servers::class) ->assertOk() // the fleet list still renders ->assertDontSee(__('servers.add_server')); // but the create control is gone } // ── operate: Services\Index start/stop/restart action buttons ─────────────── /** Stubs the systemd read so the list renders with one service row. */ private function stubServices(): void { $fleet = Mockery::mock(FleetService::class); $fleet->shouldReceive('systemd')->andReturn([ 'services' => [['name' => 'nginx.service', 'status' => 'offline', 'sub' => 'dead', 'enabled' => true, 'desc' => 'web']], 'journal' => [], 'cursor' => null, ]); app()->instance(FleetService::class, $fleet); } public function test_operator_sees_a_service_action_control(): void { $this->actingAs($this->operator()); $this->activeServer(); $this->stubServices(); Livewire::test(Services::class) ->call('load') ->assertOk() ->assertSee(__('services.restart')); // "Neustart" — a per-row action button } public function test_viewer_does_not_see_a_service_action_control(): void { $this->actingAs($this->viewer()); $this->activeServer(); $this->stubServices(); Livewire::test(Services::class) ->call('load') ->assertOk() ->assertSee('nginx.service') // the list itself is visible ->assertDontSee(__('services.restart')); // but the action buttons are gone } // ── manage-network: sidebar WireGuard nav item ────────────────────────────── public function test_admin_sees_the_wireguard_nav(): void { $this->actingAs($this->admin()); $this->get('/servers') ->assertOk() ->assertSee(__('shell.nav_wireguard')); // "WireGuard" nav item present } public function test_viewer_does_not_see_the_wireguard_nav(): void { $this->actingAs($this->viewer()); $this->get('/servers') ->assertOk() ->assertDontSee(__('shell.nav_wireguard')); // hidden for a viewer } }