mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-19 17:34:45 +02:00
Pest Test Improvements (#1137)
Co-authored-by: Lance Pioch <git@lance.sh> Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
This commit is contained in:
parent
b39a8186ae
commit
2046fa453a
3
.gitignore
vendored
3
.gitignore
vendored
@ -24,8 +24,7 @@ yarn-error.log
|
|||||||
/.vscode
|
/.vscode
|
||||||
|
|
||||||
public/assets/manifest.json
|
public/assets/manifest.json
|
||||||
/database/*.sqlite
|
/database/*.sqlite*
|
||||||
/database/*.sqlite-journal
|
|
||||||
filament-monaco-editor/
|
filament-monaco-editor/
|
||||||
_ide_helper*
|
_ide_helper*
|
||||||
/.phpstorm.meta.php
|
/.phpstorm.meta.php
|
||||||
|
@ -14,4 +14,24 @@ enum RolePermissionModels: string
|
|||||||
case Server = 'server';
|
case Server = 'server';
|
||||||
case User = 'user';
|
case User = 'user';
|
||||||
case Webhook = 'webhook';
|
case Webhook = 'webhook';
|
||||||
|
|
||||||
|
public function viewAny(): string
|
||||||
|
{
|
||||||
|
return RolePermissionPrefixes::ViewAny->value . ' ' . $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(): string
|
||||||
|
{
|
||||||
|
return RolePermissionPrefixes::View->value . ' ' . $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(): string
|
||||||
|
{
|
||||||
|
return RolePermissionPrefixes::Create->value . ' ' . $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(): string
|
||||||
|
{
|
||||||
|
return RolePermissionPrefixes::Update->value . ' ' . $this->value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,26 +36,22 @@ class SettingsController extends ClientApiController
|
|||||||
$name = $request->input('name');
|
$name = $request->input('name');
|
||||||
$description = $request->has('description') ? (string) $request->input('description') : $server->description;
|
$description = $request->has('description') ? (string) $request->input('description') : $server->description;
|
||||||
|
|
||||||
$server->name = $name;
|
|
||||||
|
|
||||||
if (config('panel.editable_server_descriptions')) {
|
|
||||||
$server->description = $description;
|
|
||||||
}
|
|
||||||
|
|
||||||
$server->save();
|
|
||||||
|
|
||||||
if ($server->name !== $name) {
|
if ($server->name !== $name) {
|
||||||
Activity::event('server:settings.rename')
|
Activity::event('server:settings.rename')
|
||||||
->property(['old' => $server->name, 'new' => $name])
|
->property(['old' => $server->name, 'new' => $name])
|
||||||
->log();
|
->log();
|
||||||
|
$server->name = $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($server->description !== $description) {
|
if ($server->description !== $description && config('panel.editable_server_descriptions')) {
|
||||||
Activity::event('server:settings.description')
|
Activity::event('server:settings.description')
|
||||||
->property(['old' => $server->description, 'new' => $description])
|
->property(['old' => $server->description, 'new' => $description])
|
||||||
->log();
|
->log();
|
||||||
|
$server->description = $description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$server->save();
|
||||||
|
|
||||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,12 +4,13 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Contracts\Validatable;
|
use App\Contracts\Validatable;
|
||||||
use App\Traits\HasValidation;
|
use App\Traits\HasValidation;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
class Permission extends Model implements Validatable
|
class Permission extends Model implements Validatable
|
||||||
{
|
{
|
||||||
use HasValidation;
|
use HasFactory, HasValidation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The resource name for this model when it is transformed into an
|
* The resource name for this model when it is transformed into an
|
||||||
|
@ -4,6 +4,7 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Enums\RolePermissionModels;
|
use App\Enums\RolePermissionModels;
|
||||||
use App\Enums\RolePermissionPrefixes;
|
use App\Enums\RolePermissionPrefixes;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Spatie\Permission\Models\Role as BaseRole;
|
use Spatie\Permission\Models\Role as BaseRole;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,6 +18,8 @@ use Spatie\Permission\Models\Role as BaseRole;
|
|||||||
*/
|
*/
|
||||||
class Role extends BaseRole
|
class Role extends BaseRole
|
||||||
{
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
public const RESOURCE_NAME = 'role';
|
public const RESOURCE_NAME = 'role';
|
||||||
|
|
||||||
public const ROOT_ADMIN = 'Root Admin';
|
public const ROOT_ADMIN = 'Root Admin';
|
||||||
|
@ -56,6 +56,8 @@
|
|||||||
"mockery/mockery": "^1.6.11",
|
"mockery/mockery": "^1.6.11",
|
||||||
"nunomaduro/collision": "^8.6",
|
"nunomaduro/collision": "^8.6",
|
||||||
"pestphp/pest": "^3.7",
|
"pestphp/pest": "^3.7",
|
||||||
|
"pestphp/pest-plugin-faker": "^3.0",
|
||||||
|
"pestphp/pest-plugin-livewire": "^3.0",
|
||||||
"spatie/laravel-ignition": "^2.9"
|
"spatie/laravel-ignition": "^2.9"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
135
composer.lock
generated
135
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "e78193e058fd9f763da97bbc11934c2d",
|
"content-hash": "556cda6914cb34938f738f77ab4b6949",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "abdelhamiderrahmouni/filament-monaco-editor",
|
"name": "abdelhamiderrahmouni/filament-monaco-editor",
|
||||||
@ -13616,6 +13616,137 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-04-16T22:59:48+00:00"
|
"time": "2025-04-16T22:59:48+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "pestphp/pest-plugin-faker",
|
||||||
|
"version": "v3.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/pestphp/pest-plugin-faker.git",
|
||||||
|
"reference": "48343e2806cfc12a042dead90ffff4a043167e3e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/pestphp/pest-plugin-faker/zipball/48343e2806cfc12a042dead90ffff4a043167e3e",
|
||||||
|
"reference": "48343e2806cfc12a042dead90ffff4a043167e3e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"fakerphp/faker": "^1.23.1",
|
||||||
|
"pestphp/pest": "^3.0.0",
|
||||||
|
"php": "^8.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"pestphp/pest-dev-tools": "^3.0.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/Faker.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Pest\\Faker\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "The Pest Faker Plugin",
|
||||||
|
"keywords": [
|
||||||
|
"faker",
|
||||||
|
"framework",
|
||||||
|
"pest",
|
||||||
|
"php",
|
||||||
|
"plugin",
|
||||||
|
"test",
|
||||||
|
"testing",
|
||||||
|
"unit"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/pestphp/pest-plugin-faker/tree/v3.0.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nunomaduro",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.patreon.com/nunomaduro",
|
||||||
|
"type": "patreon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-09-08T23:56:08+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pestphp/pest-plugin-livewire",
|
||||||
|
"version": "v3.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/pestphp/pest-plugin-livewire.git",
|
||||||
|
"reference": "e2f2edb0a7d414d6837d87908a0e148256d3bf89"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/pestphp/pest-plugin-livewire/zipball/e2f2edb0a7d414d6837d87908a0e148256d3bf89",
|
||||||
|
"reference": "e2f2edb0a7d414d6837d87908a0e148256d3bf89",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"livewire/livewire": "^3.5.6",
|
||||||
|
"pestphp/pest": "^3.0.0",
|
||||||
|
"php": "^8.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"orchestra/testbench": "^9.4.0",
|
||||||
|
"pestphp/pest-dev-tools": "^3.0.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/Autoload.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Pest\\Livewire\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "The Pest Livewire Plugin",
|
||||||
|
"keywords": [
|
||||||
|
"framework",
|
||||||
|
"livewire",
|
||||||
|
"pest",
|
||||||
|
"php",
|
||||||
|
"plugin",
|
||||||
|
"test",
|
||||||
|
"testing",
|
||||||
|
"unit"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/pestphp/pest-plugin-livewire/tree/v3.0.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/nunomaduro",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.patreon.com/nunomaduro",
|
||||||
|
"type": "patreon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-09-09T00:05:59+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "pestphp/pest-plugin-mutate",
|
"name": "pestphp/pest-plugin-mutate",
|
||||||
"version": "v3.0.5",
|
"version": "v3.0.5",
|
||||||
@ -15775,5 +15906,5 @@
|
|||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "8.2"
|
"php": "8.2"
|
||||||
},
|
},
|
||||||
"plugin-api-version": "2.6.0"
|
"plugin-api-version": "2.3.0"
|
||||||
}
|
}
|
||||||
|
18
database/Factories/PermissionFactory.php
Normal file
18
database/Factories/PermissionFactory.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Permission;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
class PermissionFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Permission::class;
|
||||||
|
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
22
database/Factories/RoleFactory.php
Normal file
22
database/Factories/RoleFactory.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Role;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
class RoleFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Role::class;
|
||||||
|
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->faker->name(),
|
||||||
|
'guard_name' => $this->faker->name(),
|
||||||
|
'created_at' => Carbon::now(),
|
||||||
|
'updated_at' => Carbon::now(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
179
tests/Feature/SettingsControllerTest.php
Normal file
179
tests/Feature/SettingsControllerTest.php
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\ServerState;
|
||||||
|
use App\Http\Controllers\Api\Client\Servers\SettingsController;
|
||||||
|
use App\Models\Permission;
|
||||||
|
use App\Repositories\Daemon\DaemonServerRepository;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
pest()->group('API');
|
||||||
|
|
||||||
|
covers(SettingsController::class);
|
||||||
|
|
||||||
|
it('server name cannot be changed', function () {
|
||||||
|
[$user, $server] = generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]);
|
||||||
|
$originalName = $server->name;
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->post("/api/client/servers/$server->uuid/settings/rename", [
|
||||||
|
'name' => 'Test Server Name',
|
||||||
|
])
|
||||||
|
->assertStatus(Response::HTTP_FORBIDDEN);
|
||||||
|
|
||||||
|
$server = $server->refresh();
|
||||||
|
expect()->toLogActivities(0)
|
||||||
|
->and($server->name)->toBe($originalName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('server description can be changed', function () {
|
||||||
|
[$user, $server] = generateTestAccount([Permission::ACTION_SETTINGS_DESCRIPTION]);
|
||||||
|
$originalDescription = $server->description;
|
||||||
|
|
||||||
|
$newDescription = 'Test Server Description';
|
||||||
|
$this->actingAs($user)
|
||||||
|
->post("/api/client/servers/$server->uuid/settings/description", [
|
||||||
|
'description' => $newDescription,
|
||||||
|
])
|
||||||
|
->assertStatus(Response::HTTP_NO_CONTENT);
|
||||||
|
|
||||||
|
$server = $server->refresh();
|
||||||
|
$logged = \App\Models\ActivityLog::first();
|
||||||
|
expect()->toLogActivities(1)
|
||||||
|
->and($logged->properties['old'])->toBe($originalDescription)
|
||||||
|
->and($logged->properties['new'])->toBe($newDescription)
|
||||||
|
->and($server->description)->not()->toBe($originalDescription);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('server description cannot be changed', function () {
|
||||||
|
[$user, $server] = generateTestAccount([Permission::ACTION_SETTINGS_DESCRIPTION]);
|
||||||
|
Config::set('panel.editable_server_descriptions', false);
|
||||||
|
$originalDescription = $server->description;
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->post("/api/client/servers/$server->uuid/settings/description", [
|
||||||
|
'description' => 'Test Description',
|
||||||
|
])
|
||||||
|
->assertStatus(Response::HTTP_NO_CONTENT);
|
||||||
|
|
||||||
|
$server = $server->refresh();
|
||||||
|
expect()->toLogActivities(0)
|
||||||
|
->and($server->description)->toBe($originalDescription);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('server name can be changed', function () {
|
||||||
|
[$user, $server] = generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT, Permission::ACTION_SETTINGS_RENAME]);
|
||||||
|
$originalName = $server->name;
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->post("/api/client/servers/$server->uuid/settings/rename", [
|
||||||
|
'name' => 'Test Server Name',
|
||||||
|
])
|
||||||
|
->assertStatus(Response::HTTP_NO_CONTENT);
|
||||||
|
|
||||||
|
$server = $server->refresh();
|
||||||
|
expect()->toLogActivities(1)
|
||||||
|
->and($server->name)->not()->toBe($originalName);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unauthorized user cannot change docker image in use by server', function () {
|
||||||
|
[$user, $server] = generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]);
|
||||||
|
$originalImage = $server->image;
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->put("/api/client/servers/$server->uuid/settings/docker-image", [
|
||||||
|
'docker_image' => 'ghcr.io/pelican-dev/yolks:java_21',
|
||||||
|
])
|
||||||
|
->assertStatus(Response::HTTP_FORBIDDEN);
|
||||||
|
|
||||||
|
$server = $server->refresh();
|
||||||
|
expect()->toLogActivities(0)
|
||||||
|
->and($server->image)->toBe($originalImage);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cannot change docker image to image not allowed by egg', function () {
|
||||||
|
|
||||||
|
[$user, $server] = generateTestAccount([Permission::ACTION_STARTUP_DOCKER_IMAGE]);
|
||||||
|
$server->image = 'ghcr.io/parkervcp/yolks:java_17';
|
||||||
|
$server->save();
|
||||||
|
|
||||||
|
$newImage = 'ghcr.io/parkervcp/fake:image';
|
||||||
|
|
||||||
|
$server = $server->refresh();
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->putJson("/api/client/servers/$server->uuid/settings/docker-image", [
|
||||||
|
'docker_image' => $newImage,
|
||||||
|
])
|
||||||
|
->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||||
|
|
||||||
|
$server->refresh();
|
||||||
|
expect()->toLogActivities(0)
|
||||||
|
->and($server->image)->not()->toBe($newImage);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can change docker image in use by server', function () {
|
||||||
|
[$user, $server] = generateTestAccount([Permission::ACTION_STARTUP_DOCKER_IMAGE]);
|
||||||
|
$oldImage = 'ghcr.io/parkervcp/yolks:java_17';
|
||||||
|
$server->image = $oldImage;
|
||||||
|
$server->save();
|
||||||
|
|
||||||
|
$newImage = 'ghcr.io/parkervcp/yolks:java_21';
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->putJson("/api/client/servers/$server->uuid/settings/docker-image", [
|
||||||
|
'docker_image' => $newImage,
|
||||||
|
])
|
||||||
|
->assertStatus(Response::HTTP_NO_CONTENT);
|
||||||
|
|
||||||
|
$server = $server->refresh();
|
||||||
|
|
||||||
|
$logItem = \App\Models\ActivityLog::first();
|
||||||
|
expect()->toLogActivities(1)
|
||||||
|
->and($logItem->properties['old'])->toBe($oldImage)
|
||||||
|
->and($logItem->properties['new'])->toBe($newImage)
|
||||||
|
->and($server->image)->toBe($newImage);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unable to change the docker image set by administrator', function () {
|
||||||
|
[$user, $server] = generateTestAccount([Permission::ACTION_STARTUP_DOCKER_IMAGE]);
|
||||||
|
$oldImage = 'ghcr.io/parkervcp/yolks:java_custom';
|
||||||
|
$server->image = $oldImage;
|
||||||
|
$server->save();
|
||||||
|
|
||||||
|
$newImage = 'ghcr.io/parkervcp/yolks:java_8';
|
||||||
|
|
||||||
|
$this->actingAs($user)
|
||||||
|
->putJson("/api/client/servers/$server->uuid/settings/docker-image", [
|
||||||
|
'docker_image' => $newImage,
|
||||||
|
])
|
||||||
|
->assertStatus(Response::HTTP_BAD_REQUEST);
|
||||||
|
|
||||||
|
$server = $server->refresh();
|
||||||
|
|
||||||
|
expect()->toLogActivities(0)
|
||||||
|
->and($server->image)->toBe($oldImage);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can be reinstalled', function () {
|
||||||
|
[$user, $server] = generateTestAccount([Permission::ACTION_SETTINGS_REINSTALL]);
|
||||||
|
expect($server->isInstalled())->toBeTrue();
|
||||||
|
|
||||||
|
$service = \Mockery::mock(DaemonServerRepository::class);
|
||||||
|
$this->app->instance(DaemonServerRepository::class, $service);
|
||||||
|
|
||||||
|
$service->expects('setServer')
|
||||||
|
->with(\Mockery::on(function ($value) use ($server) {
|
||||||
|
return $value->uuid === $server->uuid;
|
||||||
|
}))
|
||||||
|
->andReturnSelf()
|
||||||
|
->getMock()
|
||||||
|
->expects('reinstall')
|
||||||
|
->andReturnUndefined();
|
||||||
|
|
||||||
|
$this->actingAs($user)->postJson("/api/client/servers/$server->uuid/settings/reinstall")
|
||||||
|
->assertStatus(Response::HTTP_ACCEPTED);
|
||||||
|
|
||||||
|
$server = $server->refresh();
|
||||||
|
expect()->toLogActivities(1)
|
||||||
|
->and($server->status)->toBe(ServerState::Installing);
|
||||||
|
});
|
49
tests/Filament/Admin/ListEggsTest.php
Normal file
49
tests/Filament/Admin/ListEggsTest.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\RolePermissionModels;
|
||||||
|
use App\Filament\Admin\Resources\EggResource\Pages\ListEggs;
|
||||||
|
use App\Models\Egg;
|
||||||
|
use App\Models\Permission;
|
||||||
|
use App\Models\Role;
|
||||||
|
|
||||||
|
use function Pest\Livewire\livewire;
|
||||||
|
|
||||||
|
it('root admin can see all eggs', function () {
|
||||||
|
$eggs = Egg::all();
|
||||||
|
[$admin] = generateTestAccount([]);
|
||||||
|
$admin = $admin->syncRoles(Role::getRootAdmin());
|
||||||
|
|
||||||
|
$this->actingAs($admin);
|
||||||
|
livewire(ListEggs::class)
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertCountTableRecords($eggs->count())
|
||||||
|
->assertCanSeeTableRecords($eggs);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('non root admin cannot see any eggs', function () {
|
||||||
|
$role = Role::factory()->create(['name' => 'Node Viewer', 'guard_name' => 'web']);
|
||||||
|
// Node Permission is on purpose, we check the wrong permissions.
|
||||||
|
$permission = Permission::factory()->create(['name' => RolePermissionModels::Node->viewAny(), 'guard_name' => 'web']);
|
||||||
|
$role->permissions()->attach($permission);
|
||||||
|
[$user] = generateTestAccount([]);
|
||||||
|
|
||||||
|
$this->actingAs($user);
|
||||||
|
livewire(ListEggs::class)
|
||||||
|
->assertForbidden();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('non root admin with permissions can see eggs', function () {
|
||||||
|
$role = Role::factory()->create(['name' => 'Egg Viewer', 'guard_name' => 'web']);
|
||||||
|
$permission = Permission::factory()->create(['name' => RolePermissionModels::Egg->viewAny(), 'guard_name' => 'web']);
|
||||||
|
$role->permissions()->attach($permission);
|
||||||
|
|
||||||
|
$eggs = Egg::all();
|
||||||
|
[$user] = generateTestAccount([]);
|
||||||
|
$user = $user->syncRoles($role);
|
||||||
|
|
||||||
|
$this->actingAs($user);
|
||||||
|
livewire(ListEggs::class)
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertCountTableRecords($eggs->count())
|
||||||
|
->assertCanSeeTableRecords($eggs);
|
||||||
|
});
|
67
tests/Filament/Admin/ListNodesTest.php
Normal file
67
tests/Filament/Admin/ListNodesTest.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\RolePermissionModels;
|
||||||
|
use App\Filament\Admin\Resources\NodeResource\Pages\ListNodes;
|
||||||
|
use App\Models\Node;
|
||||||
|
use App\Models\Permission;
|
||||||
|
use App\Models\Role;
|
||||||
|
use App\Models\Server;
|
||||||
|
use Filament\Actions\CreateAction;
|
||||||
|
use Filament\Tables\Actions\CreateAction as TableCreateAction;
|
||||||
|
|
||||||
|
use function Pest\Livewire\livewire;
|
||||||
|
|
||||||
|
it('root admin can see all nodes', function () {
|
||||||
|
[$admin] = generateTestAccount([]);
|
||||||
|
$admin = $admin->syncRoles(Role::getRootAdmin());
|
||||||
|
$nodes = Node::all();
|
||||||
|
|
||||||
|
$this->actingAs($admin);
|
||||||
|
livewire(ListNodes::class)
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertCountTableRecords($nodes->count())
|
||||||
|
->assertCanSeeTableRecords($nodes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('non root admin cannot see any nodes', function () {
|
||||||
|
$role = Role::factory()->create(['name' => 'Egg Viewer', 'guard_name' => 'web']);
|
||||||
|
// Egg Permission is on purpose, we check the wrong permissions.
|
||||||
|
$permission = Permission::factory()->create(['name' => RolePermissionModels::Egg->viewAny(), 'guard_name' => 'web']);
|
||||||
|
$role->permissions()->attach($permission);
|
||||||
|
[$user] = generateTestAccount();
|
||||||
|
|
||||||
|
$this->actingAs($user);
|
||||||
|
livewire(ListNodes::class)
|
||||||
|
->assertForbidden();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('non root admin with permissions can see nodes', function () {
|
||||||
|
$role = Role::factory()->create(['name' => 'Node Viewer', 'guard_name' => 'web']);
|
||||||
|
$permission = Permission::factory()->create(['name' => RolePermissionModels::Node->viewAny(), 'guard_name' => 'web']);
|
||||||
|
$role->permissions()->attach($permission);
|
||||||
|
|
||||||
|
[$user] = generateTestAccount();
|
||||||
|
$nodes = Node::all();
|
||||||
|
$user = $user->syncRoles($role);
|
||||||
|
|
||||||
|
$this->actingAs($user);
|
||||||
|
livewire(ListNodes::class)
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertCountTableRecords($nodes->count())
|
||||||
|
->assertCanSeeTableRecords($nodes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the create button in the table instead of the header when 0 nodes', function () {
|
||||||
|
[$admin] = generateTestAccount([]);
|
||||||
|
$admin = $admin->syncRoles(Role::getRootAdmin());
|
||||||
|
|
||||||
|
// Nuke servers & nodes
|
||||||
|
Server::truncate();
|
||||||
|
Node::truncate();
|
||||||
|
|
||||||
|
$this->actingAs($admin);
|
||||||
|
livewire(ListNodes::class)
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertHeaderMissing(CreateAction::class)
|
||||||
|
->assertActionExists(TableCreateAction::class);
|
||||||
|
});
|
132
tests/Pest.php
132
tests/Pest.php
@ -24,10 +24,26 @@
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use App\Models\ActivityLog;
|
||||||
|
use App\Models\Allocation;
|
||||||
|
use App\Models\Egg;
|
||||||
|
use App\Models\Node;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\Models\Subuser;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Tests\Integration\IntegrationTestCase;
|
||||||
|
use Ramsey\Uuid\Uuid;
|
||||||
|
|
||||||
expect()->extend('toBeOne', function () {
|
expect()->extend('toBeOne', function () {
|
||||||
return $this->toBe(1);
|
return $this->toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect()->extend('toLogActivities', function (int $times) {
|
||||||
|
expect(ActivityLog::count())->toBe($times);
|
||||||
|
});
|
||||||
|
|
||||||
|
uses(IntegrationTestCase::class)->in('Feature', 'Filament');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Functions
|
| Functions
|
||||||
@ -43,3 +59,119 @@ function something()
|
|||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a user and a server for that user. If an array of permissions is passed it
|
||||||
|
* is assumed that the user is actually a subuser of the server.
|
||||||
|
*
|
||||||
|
* @param string[] $permissions
|
||||||
|
* @return array{\App\Models\User, \App\Models\Server}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a server model in the databases for the purpose of testing. If an attribute
|
||||||
|
* is passed in that normally requires this function to create a model no model will be
|
||||||
|
* created and that attribute's value will be used.
|
||||||
|
*
|
||||||
|
* The returned server model will have all the relationships loaded onto it.
|
||||||
|
*/
|
||||||
|
function createServerModel(array $attributes = []): Server
|
||||||
|
{
|
||||||
|
if (isset($attributes['user_id'])) {
|
||||||
|
$attributes['owner_id'] = $attributes['user_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($attributes['owner_id'])) {
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$attributes['owner_id'] = $user->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($attributes['node_id'])) {
|
||||||
|
/** @var \App\Models\Node $node */
|
||||||
|
$node = Node::factory()->create();
|
||||||
|
$attributes['node_id'] = $node->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($attributes['allocation_id'])) {
|
||||||
|
/** @var \App\Models\Allocation $allocation */
|
||||||
|
$allocation = Allocation::factory()->create(['node_id' => $attributes['node_id']]);
|
||||||
|
$attributes['allocation_id'] = $allocation->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($attributes['egg_id'])) {
|
||||||
|
$egg = getBungeecordEgg();
|
||||||
|
|
||||||
|
$attributes['egg_id'] = $egg->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($attributes['user_id']);
|
||||||
|
|
||||||
|
/** @var \App\Models\Server $server */
|
||||||
|
$server = Server::factory()->create($attributes);
|
||||||
|
|
||||||
|
Allocation::query()->where('id', $server->allocation_id)->update(['server_id' => $server->id]);
|
||||||
|
|
||||||
|
return $server->fresh([
|
||||||
|
'user', 'node', 'allocation', 'egg',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a user and a server for that user. If an array of permissions is passed it
|
||||||
|
* is assumed that the user is actually a subuser of the server.
|
||||||
|
*
|
||||||
|
* @param string[] $permissions
|
||||||
|
* @return array{\App\Models\User, \App\Models\Server}
|
||||||
|
*/
|
||||||
|
function generateTestAccount(array $permissions = []): array
|
||||||
|
{
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
if (empty($permissions)) {
|
||||||
|
return [$user, createServerModel(['user_id' => $user->id])];
|
||||||
|
}
|
||||||
|
|
||||||
|
$server = createServerModel();
|
||||||
|
|
||||||
|
Subuser::query()->create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'server_id' => $server->id,
|
||||||
|
'permissions' => $permissions,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return [$user, $server];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones a given egg allowing us to make modifications that don't affect other
|
||||||
|
* tests that rely on the egg existing in the correct state.
|
||||||
|
*/
|
||||||
|
function cloneEggAndVariables(Egg $egg): Egg
|
||||||
|
{
|
||||||
|
$model = $egg->replicate(['id', 'uuid']);
|
||||||
|
$model->uuid = Uuid::uuid4()->toString();
|
||||||
|
$model->push();
|
||||||
|
|
||||||
|
/** @var \App\Models\Egg $model */
|
||||||
|
$model = $model->fresh();
|
||||||
|
|
||||||
|
foreach ($egg->variables as $variable) {
|
||||||
|
$variable->replicate(['id', 'egg_id'])->forceFill(['egg_id' => $model->id])->push();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $model->fresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Almost every test just assumes it is using BungeeCord — this is the critical
|
||||||
|
* egg model for all tests unless specified otherwise.
|
||||||
|
*/
|
||||||
|
function getBungeecordEgg(): Egg
|
||||||
|
{
|
||||||
|
/** @var \App\Models\Egg $egg */
|
||||||
|
$egg = Egg::query()->where('author', 'panel@example.com')->where('name', 'Bungeecord')->firstOrFail();
|
||||||
|
|
||||||
|
return $egg;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user