139 lines
4.7 KiB
PHP
139 lines
4.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Livewire\Servers\Index as Servers;
|
|
use App\Livewire\Services\Index as Services;
|
|
use App\Models\Server;
|
|
use App\Models\User;
|
|
use App\Services\FleetService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
use Mockery;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* RBAC view-level HIDING: operators/viewers must not SEE the controls they would be
|
|
* 403'd from (the server-side abort_unless guards are proven in the Rbac*GateTest
|
|
* suites). This only asserts the `@can(...)` wrappers render/omit the button markup —
|
|
* a light UX check across a few representative surfaces, one per ability family:
|
|
* - Servers\Index add-server trigger → manage-fleet (admin sees, viewer does not);
|
|
* - Services\Index start/stop/restart → operate (operator sees, viewer does not);
|
|
* - the sidebar WireGuard nav item → manage-network(admin sees, viewer does not).
|
|
* Every SSH read is mocked — no real host is ever touched.
|
|
*/
|
|
class RbacUiHidingTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
Mockery::close();
|
|
parent::tearDown();
|
|
}
|
|
|
|
private function admin(): User
|
|
{
|
|
return User::factory()->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
|
|
}
|
|
}
|