migrating tests (DatabaseUser, Firewall and Logs)

This commit is contained in:
Saeed Vaziry 2024-10-09 23:32:52 +02:00
parent 0007e6f00a
commit 93cee92568
12 changed files with 214 additions and 56 deletions

View File

@ -3,7 +3,6 @@
namespace App\Actions\Server; namespace App\Actions\Server;
use App\Models\Server; use App\Models\Server;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
class CreateServerLog class CreateServerLog
@ -13,8 +12,6 @@ class CreateServerLog
*/ */
public function create(Server $server, array $input): void public function create(Server $server, array $input): void
{ {
$this->validate($input);
$server->logs()->create([ $server->logs()->create([
'is_remote' => true, 'is_remote' => true,
'name' => $input['path'], 'name' => $input['path'],
@ -23,13 +20,10 @@ public function create(Server $server, array $input): void
]); ]);
} }
/** public static function rules(): array
* @throws ValidationException
*/
protected function validate(array $input): void
{ {
Validator::make($input, [ return [
'path' => 'required', 'path' => 'required',
])->validate(); ];
} }
} }

View File

@ -155,9 +155,24 @@ public function upload(string $local, string $remote): void
if (! $this->connection) { if (! $this->connection) {
$this->connect(true); $this->connect(true);
} }
$this->connection->put($remote, $local, SFTP::SOURCE_LOCAL_FILE); $this->connection->put($remote, $local, SFTP::SOURCE_LOCAL_FILE);
} }
/**
* @throws Throwable
*/
public function download(string $local, string $remote): void
{
$this->log = null;
if (! $this->connection) {
$this->connect(true);
}
$this->connection->get($remote, $local, SFTP::SOURCE_LOCAL_FILE);
}
/** /**
* @throws Exception * @throws Exception
*/ */

View File

@ -5,10 +5,12 @@
use Exception; use Exception;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpFoundation\StreamedResponse;
use Throwable;
/** /**
* @property int $server_id * @property int $server_id
@ -44,6 +46,7 @@ public static function boot(): void
parent::boot(); parent::boot();
static::deleting(function (ServerLog $log) { static::deleting(function (ServerLog $log) {
if ($log->is_remote) {
try { try {
if (Storage::disk($log->disk)->exists($log->name)) { if (Storage::disk($log->disk)->exists($log->name)) {
Storage::disk($log->disk)->delete($log->name); Storage::disk($log->disk)->delete($log->name);
@ -51,6 +54,7 @@ public static function boot(): void
} catch (Exception $e) { } catch (Exception $e) {
Log::error($e->getMessage(), ['exception' => $e]); Log::error($e->getMessage(), ['exception' => $e]);
} }
}
}); });
} }
@ -69,8 +73,28 @@ public function site(): BelongsTo
return $this->belongsTo(Site::class); return $this->belongsTo(Site::class);
} }
/**
* @throws Throwable
*/
public function download(): StreamedResponse public function download(): StreamedResponse
{ {
if ($this->is_remote) {
$tmpName = $this->server->id.'-'.strtotime('now').'-'.$this->type.'.log';
$tmpPath = Storage::disk('local')->path($tmpName);
$this->server->ssh()->download($tmpPath, $this->name);
dispatch(function () use ($tmpPath) {
if (File::exists($tmpPath)) {
File::delete($tmpPath);
}
})
->delay(now()->addMinutes(5))
->onQueue('default');
return Storage::disk('local')->download($tmpName, str($this->name)->afterLast('/'));
}
return Storage::disk($this->disk)->download($this->name); return Storage::disk($this->disk)->download($this->name);
} }

View File

@ -30,7 +30,7 @@ public function mount(): void
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Action::make('Create User') Action::make('create')
->icon('heroicon-o-plus') ->icon('heroicon-o-plus')
->modalWidth(MaxWidth::Large) ->modalWidth(MaxWidth::Large)
->authorize(fn () => auth()->user()?->can('create', [DatabaseUser::class, $this->server])) ->authorize(fn () => auth()->user()?->can('create', [DatabaseUser::class, $this->server]))

View File

@ -3,11 +3,14 @@
namespace App\Web\Pages\Servers\Logs; namespace App\Web\Pages\Servers\Logs;
use App\Models\ServerLog; use App\Models\ServerLog;
use App\Web\Contracts\HasSecondSubNav;
use App\Web\Pages\Servers\Logs\Widgets\LogsList; use App\Web\Pages\Servers\Logs\Widgets\LogsList;
use App\Web\Pages\Servers\Page; use App\Web\Pages\Servers\Page;
class Index extends Page class Index extends Page implements HasSecondSubNav
{ {
use Traits\Navigation;
protected static ?string $slug = 'servers/{server}/logs'; protected static ?string $slug = 'servers/{server}/logs';
protected static ?string $title = 'Logs'; protected static ?string $title = 'Logs';

View File

@ -0,0 +1,54 @@
<?php
namespace App\Web\Pages\Servers\Logs;
use App\Actions\Server\CreateServerLog;
use App\Models\ServerLog;
use App\Web\Contracts\HasSecondSubNav;
use App\Web\Pages\Servers\Logs\Widgets\LogsList;
use App\Web\Pages\Servers\Page;
use Filament\Actions\Action;
use Filament\Forms\Components\TextInput;
use Filament\Support\Enums\MaxWidth;
class RemoteLogs extends Page implements HasSecondSubNav
{
use Traits\Navigation;
protected static ?string $slug = 'servers/{server}/logs/remote';
protected static ?string $title = 'Remote Logs';
public function mount(): void
{
$this->authorize('viewAny', [ServerLog::class, $this->server]);
}
public function getWidgets(): array
{
return [
[LogsList::class, ['server' => $this->server, 'remote' => true]],
];
}
protected function getHeaderActions(): array
{
return [
Action::make('create')
->icon('heroicon-o-plus')
->modalWidth(MaxWidth::Large)
->authorize(fn () => auth()->user()?->can('create', [ServerLog::class, $this->server]))
->form([
TextInput::make('path')
->helperText('The full path of the log file on the server')
->rules(fn (callable $get) => CreateServerLog::rules()['path']),
])
->modalSubmitActionLabel('Create')
->action(function (array $data) {
app(CreateServerLog::class)->create($this->server, $data);
$this->dispatch('$refresh');
}),
];
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Web\Pages\Servers\Logs\Traits;
use App\Models\ServerLog;
use App\Web\Pages\Servers\Logs\Index;
use App\Web\Pages\Servers\Logs\RemoteLogs;
use Filament\Navigation\NavigationGroup;
use Filament\Navigation\NavigationItem;
trait Navigation
{
public function getSecondSubNavigation(): array
{
$items = [];
if (auth()->user()->can('viewAny', [ServerLog::class, $this->server])) {
$items[] = NavigationItem::make(Index::getNavigationLabel())
->icon('heroicon-o-square-3-stack-3d')
->isActiveWhen(fn () => request()->routeIs(Index::getRouteName()))
->url(Index::getUrl(parameters: ['server' => $this->server]));
$items[] = NavigationItem::make(RemoteLogs::getNavigationLabel())
->icon('heroicon-o-wifi')
->isActiveWhen(fn () => request()->routeIs(RemoteLogs::getRouteName()))
->url(RemoteLogs::getUrl(parameters: ['server' => $this->server]));
}
return [
NavigationGroup::make()
->items($items),
];
}
}

View File

@ -26,6 +26,8 @@ class LogsList extends Widget
public ?string $label = ''; public ?string $label = '';
public bool $remote = false;
protected $listeners = ['$refresh']; protected $listeners = ['$refresh'];
protected function getTableQuery(): Builder protected function getTableQuery(): Builder
@ -36,7 +38,8 @@ protected function getTableQuery(): Builder
if ($this->site) { if ($this->site) {
$query->where('site_id', $this->site->id); $query->where('site_id', $this->site->id);
} }
}); })
->where('is_remote', $this->remote);
} }
protected function getTableColumns(): array protected function getTableColumns(): array

View File

@ -185,6 +185,7 @@
/* /*
* Package Service Providers... * Package Service Providers...
*/ */
Illuminate\Concurrency\ConcurrencyServiceProvider::class,
/* /*
* Application Service Providers... * Application Service Providers...

View File

@ -5,7 +5,10 @@
use App\Enums\DatabaseUserStatus; use App\Enums\DatabaseUserStatus;
use App\Facades\SSH; use App\Facades\SSH;
use App\Models\DatabaseUser; use App\Models\DatabaseUser;
use App\Web\Pages\Servers\Databases\Users;
use App\Web\Pages\Servers\Databases\Widgets\DatabaseUsersList;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase; use Tests\TestCase;
class DatabaseUserTest extends TestCase class DatabaseUserTest extends TestCase
@ -18,10 +21,14 @@ public function test_create_database_user(): void
SSH::fake(); SSH::fake();
$this->post(route('servers.databases.users.store', $this->server), [ Livewire::test(Users::class, [
'server' => $this->server,
])
->callAction('create', [
'username' => 'user', 'username' => 'user',
'password' => 'password', 'password' => 'password',
])->assertSessionDoesntHaveErrors(); ])
->assertSuccessful();
$this->assertDatabaseHas('database_users', [ $this->assertDatabaseHas('database_users', [
'username' => 'user', 'username' => 'user',
@ -35,12 +42,16 @@ public function test_create_database_user_with_remote(): void
SSH::fake(); SSH::fake();
$this->post(route('servers.databases.users.store', $this->server), [ Livewire::test(Users::class, [
'server' => $this->server,
])
->callAction('create', [
'username' => 'user', 'username' => 'user',
'password' => 'password', 'password' => 'password',
'remote' => 'on', 'remote' => true,
'host' => '%', 'host' => '%',
])->assertSessionDoesntHaveErrors(); ])
->assertSuccessful();
$this->assertDatabaseHas('database_users', [ $this->assertDatabaseHas('database_users', [
'username' => 'user', 'username' => 'user',
@ -57,7 +68,11 @@ public function test_see_database_users_list(): void
'server_id' => $this->server, 'server_id' => $this->server,
]); ]);
$this->get(route('servers.databases', $this->server)) $this->get(
Users::getUrl([
'server' => $this->server,
])
)
->assertSuccessful() ->assertSuccessful()
->assertSee($databaseUser->username); ->assertSee($databaseUser->username);
} }
@ -72,8 +87,11 @@ public function test_delete_database_user(): void
'server_id' => $this->server, 'server_id' => $this->server,
]); ]);
$this->delete(route('servers.databases.users.destroy', [$this->server, $databaseUser])) Livewire::test(DatabaseUsersList::class, [
->assertSessionDoesntHaveErrors(); 'server' => $this->server,
])
->callTableAction('delete', $databaseUser->id)
->assertSuccessful();
$this->assertDatabaseMissing('database_users', [ $this->assertDatabaseMissing('database_users', [
'id' => $databaseUser->id, 'id' => $databaseUser->id,
@ -90,11 +108,13 @@ public function test_unlink_database(): void
'server_id' => $this->server, 'server_id' => $this->server,
]); ]);
$this->post(route('servers.databases.users.link', [ Livewire::test(DatabaseUsersList::class, [
'server' => $this->server, 'server' => $this->server,
'databaseUser' => $databaseUser, ])
]), []) ->callTableAction('link', $databaseUser->id, [
->assertSessionDoesntHaveErrors(); 'databases' => [],
])
->assertSuccessful();
$this->assertDatabaseHas('database_users', [ $this->assertDatabaseHas('database_users', [
'username' => $databaseUser->username, 'username' => $databaseUser->username,

View File

@ -5,7 +5,10 @@
use App\Enums\FirewallRuleStatus; use App\Enums\FirewallRuleStatus;
use App\Facades\SSH; use App\Facades\SSH;
use App\Models\FirewallRule; use App\Models\FirewallRule;
use App\Web\Pages\Servers\Firewall\Index;
use App\Web\Pages\Servers\Firewall\Widgets\RulesList;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase; use Tests\TestCase;
class FirewallTest extends TestCase class FirewallTest extends TestCase
@ -18,13 +21,17 @@ public function test_create_firewall_rule(): void
$this->actingAs($this->user); $this->actingAs($this->user);
$this->post(route('servers.firewall.store', $this->server), [ Livewire::test(Index::class, [
'server' => $this->server,
])
->callAction('create', [
'type' => 'allow', 'type' => 'allow',
'protocol' => 'tcp', 'protocol' => 'tcp',
'port' => '1234', 'port' => '1234',
'source' => '0.0.0.0', 'source' => '0.0.0.0',
'mask' => '0', 'mask' => '0',
])->assertSessionDoesntHaveErrors(); ])
->assertSuccessful();
$this->assertDatabaseHas('firewall_rules', [ $this->assertDatabaseHas('firewall_rules', [
'port' => '1234', 'port' => '1234',
@ -40,7 +47,7 @@ public function test_see_firewall_rules(): void
'server_id' => $this->server->id, 'server_id' => $this->server->id,
]); ]);
$this->get(route('servers.firewall', $this->server)) $this->get(Index::getUrl(['server' => $this->server]))
->assertSuccessful() ->assertSuccessful()
->assertSee($rule->source) ->assertSee($rule->source)
->assertSee($rule->port); ->assertSee($rule->port);
@ -56,10 +63,11 @@ public function test_delete_firewall_rule(): void
'server_id' => $this->server->id, 'server_id' => $this->server->id,
]); ]);
$this->delete(route('servers.firewall.destroy', [ Livewire::test(RulesList::class, [
'server' => $this->server, 'server' => $this->server,
'firewallRule' => $rule, ])
]))->assertSessionDoesntHaveErrors(); ->callTableAction('delete', $rule->id)
->assertSuccessful();
$this->assertDatabaseMissing('firewall_rules', [ $this->assertDatabaseMissing('firewall_rules', [
'id' => $rule->id, 'id' => $rule->id,

View File

@ -3,7 +3,10 @@
namespace Tests\Feature; namespace Tests\Feature;
use App\Models\ServerLog; use App\Models\ServerLog;
use App\Web\Pages\Servers\Logs\Index;
use App\Web\Pages\Servers\Logs\RemoteLogs;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Tests\TestCase; use Tests\TestCase;
class LogsTest extends TestCase class LogsTest extends TestCase
@ -19,37 +22,36 @@ public function test_see_logs()
'server_id' => $this->server->id, 'server_id' => $this->server->id,
]); ]);
$this->get(route('servers.logs', $this->server)) $this->get(Index::getUrl(['server' => $this->server]))
->assertSuccessful() ->assertSuccessful()
->assertSeeText($log->type); ->assertSee($log->name);
} }
public function test_see_logs_remote() public function test_see_logs_remote()
{ {
$this->actingAs($this->user); $this->actingAs($this->user);
/** @var ServerLog $log */ ServerLog::factory()->create([
$log = ServerLog::factory()->create([
'server_id' => $this->server->id, 'server_id' => $this->server->id,
'is_remote' => true, 'is_remote' => true,
'type' => 'remote', 'type' => 'remote',
'name' => 'see-remote-log', 'name' => 'see-remote-log',
]); ]);
$this->get(route('servers.logs.remote', $this->server)) $this->get(RemoteLogs::getUrl(['server' => $this->server]))
->assertSuccessful() ->assertSuccessful()
->assertSeeText('see-remote-log'); ->assertSee('see-remote-log');
} }
public function test_create_remote_log() public function test_create_remote_log()
{ {
$this->actingAs($this->user); $this->actingAs($this->user);
$this->post(route('servers.logs.remote.store', [ Livewire::test(RemoteLogs::class, ['server' => $this->server])
'server' => $this->server->id, ->callAction('create', [
]), [
'path' => 'test-path', 'path' => 'test-path',
])->assertOk(); ])
->assertSuccessful();
$this->assertDatabaseHas('server_logs', [ $this->assertDatabaseHas('server_logs', [
'is_remote' => true, 'is_remote' => true,