Skip to content

Commit 8cfdb3a

Browse files
committed
Add test cases for sending a command to a server
1 parent 4cb95d8 commit 8cfdb3a

File tree

8 files changed

+160
-46
lines changed

8 files changed

+160
-46
lines changed

app/Http/Requests/Api/Client/Servers/SendCommandRequest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
namespace Pterodactyl\Http\Requests\Api\Client\Servers;
44

55
use Pterodactyl\Models\Permission;
6+
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
67

7-
class SendCommandRequest extends GetServerRequest
8+
class SendCommandRequest extends ClientApiRequest
89
{
910
/**
1011
* Determine if the API user has permission to perform this action.

database/factories/ModelFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
$factory->define(Pterodactyl\Models\Server::class, function (Faker $faker) {
2222
return [
23-
'uuid' => $faker->unique()->uuid,
23+
'uuid' => Uuid::uuid4()->toString(),
2424
'uuidShort' => str_random(8),
2525
'name' => $faker->firstName,
2626
'description' => implode(' ', $faker->sentences()),

tests/Integration/Api/Client/AccountControllerTest.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,9 @@
66
use Pterodactyl\Models\User;
77
use Illuminate\Http\Response;
88
use Illuminate\Auth\AuthManager;
9-
use Pterodactyl\Tests\Integration\IntegrationTestCase;
109

11-
class AccountControllerTest extends IntegrationTestCase
10+
class AccountControllerTest extends ClientApiIntegrationTestCase
1211
{
13-
/**
14-
* Clean up after running tests.
15-
*/
16-
protected function tearDown(): void
17-
{
18-
User::query()->forceDelete();
19-
20-
parent::tearDown();
21-
}
22-
2312
/**
2413
* Test that the user's account details are returned from the account endpoint.
2514
*/

tests/Integration/Api/Client/ApiKeyControllerTest.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,15 @@
55
use Pterodactyl\Models\User;
66
use Illuminate\Http\Response;
77
use Pterodactyl\Models\ApiKey;
8-
use Pterodactyl\Tests\Integration\IntegrationTestCase;
98

10-
class ApiKeyControllerTest extends IntegrationTestCase
9+
class ApiKeyControllerTest extends ClientApiIntegrationTestCase
1110
{
1211
/**
1312
* Cleanup after tests.
1413
*/
1514
protected function tearDown(): void
1615
{
1716
ApiKey::query()->forceDelete();
18-
User::query()->forceDelete();
1917

2018
parent::tearDown();
2119
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace Pterodactyl\Tests\Integration\Api\Client;
4+
5+
use Pterodactyl\Models\Node;
6+
use Pterodactyl\Models\User;
7+
use Pterodactyl\Models\Server;
8+
use Pterodactyl\Models\Subuser;
9+
use Pterodactyl\Models\Location;
10+
use Pterodactyl\Tests\Integration\IntegrationTestCase;
11+
12+
abstract class ClientApiIntegrationTestCase extends IntegrationTestCase
13+
{
14+
/**
15+
* Cleanup after running tests.
16+
*/
17+
protected function tearDown(): void
18+
{
19+
Server::query()->forceDelete();
20+
Node::query()->forceDelete();
21+
Location::query()->forceDelete();
22+
User::query()->forceDelete();
23+
24+
parent::tearDown();
25+
}
26+
27+
/**
28+
* Generates a user and a server for that user. If an array of permissions is passed it
29+
* is assumed that the user is actually a subuser of the server.
30+
*
31+
* @param string[] $permissions
32+
* @return array
33+
*/
34+
protected function generateTestAccount(array $permissions = []): array
35+
{
36+
/** @var \Pterodactyl\Models\User $user */
37+
$user = factory(User::class)->create();
38+
39+
if (empty($permissions)) {
40+
return [$user, $this->createServerModel(['user_id' => $user->id])];
41+
}
42+
43+
$server = $this->createServerModel();
44+
45+
Subuser::query()->create([
46+
'user_id' => $user->id,
47+
'server_id' => $server->id,
48+
'permissions' => $permissions,
49+
]);
50+
51+
return [$user, $server];
52+
}
53+
}

tests/Integration/Api/Client/ClientControllerTest.php

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,13 @@
22

33
namespace Pterodactyl\Tests\Integration\Api\Client;
44

5-
use Pterodactyl\Models\Node;
65
use Pterodactyl\Models\User;
76
use Pterodactyl\Models\Server;
87
use Pterodactyl\Models\Subuser;
9-
use Pterodactyl\Models\Location;
108
use Pterodactyl\Models\Permission;
11-
use Pterodactyl\Tests\Integration\IntegrationTestCase;
129

13-
class ClientControllerTest extends IntegrationTestCase
10+
class ClientControllerTest extends ClientApiIntegrationTestCase
1411
{
15-
/**
16-
* Cleanup after tests are run.
17-
*/
18-
protected function tearDown(): void
19-
{
20-
Server::query()->forceDelete();
21-
User::query()->forceDelete();
22-
Node::query()->forceDelete();
23-
Location::query()->forceDelete();
24-
25-
parent::tearDown();
26-
}
27-
2812
/**
2913
* Test that only the servers a logged in user is assigned to are returned by the
3014
* API endpoint. Obviously there are cases such as being an administrator or being
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
namespace Pterodactyl\Tests\Integration\Api\Client\Server;
4+
5+
use Mockery;
6+
use GuzzleHttp\Psr7\Request;
7+
use Illuminate\Http\Response;
8+
use Pterodactyl\Models\Permission;
9+
use GuzzleHttp\Exception\BadResponseException;
10+
use GuzzleHttp\Psr7\Response as GuzzleResponse;
11+
use Pterodactyl\Repositories\Wings\DaemonCommandRepository;
12+
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
13+
14+
class CommandControllerTest extends ClientApiIntegrationTestCase
15+
{
16+
/** @var \Mockery\MockInterface */
17+
private $repository;
18+
19+
/**
20+
* Setup tests.
21+
*/
22+
public function setUp(): void
23+
{
24+
parent::setUp();
25+
26+
$this->repository = Mockery::mock(DaemonCommandRepository::class);
27+
$this->app->instance(DaemonCommandRepository::class, $this->repository);
28+
}
29+
30+
/**
31+
* Test that a validation error is returned if there is no command present in the
32+
* request.
33+
*/
34+
public function testValidationErrorIsReturnedIfNoCommandIsPresent()
35+
{
36+
[$user, $server] = $this->generateTestAccount();
37+
38+
$response = $this->actingAs($user)->postJson("/api/client/servers/{$server->uuid}/command", [
39+
'command' => '',
40+
]);
41+
42+
$response->assertStatus(Response::HTTP_UNPROCESSABLE_ENTITY);
43+
$response->assertJsonPath('errors.0.code', 'required');
44+
}
45+
46+
/**
47+
* Test that a subuser without the required permission receives an error when trying to
48+
* execute the command.
49+
*/
50+
public function testSubuserWithoutPermissionReceivesError()
51+
{
52+
[$user, $server] = $this->generateTestAccount([Permission::ACTION_WEBSOCKET_CONNECT]);
53+
54+
$response = $this->actingAs($user)->postJson("/api/client/servers/{$server->uuid}/command", [
55+
'command' => 'say Test',
56+
]);
57+
58+
$response->assertStatus(Response::HTTP_FORBIDDEN);
59+
}
60+
61+
/**
62+
* Test that a command can be sent to the server.
63+
*/
64+
public function testCommandCanSendToServer()
65+
{
66+
[$user, $server] = $this->generateTestAccount([Permission::ACTION_CONTROL_CONSOLE]);
67+
68+
$this->repository->expects('setServer')->with(Mockery::on(function ($value) use ($server) {
69+
return $value->uuid === $server->uuid;
70+
}))->andReturnSelf();
71+
$this->repository->expects('send')->with('say Test')->andReturn(new GuzzleResponse);
72+
73+
$response = $this->actingAs($user)->postJson("/api/client/servers/{$server->uuid}/command", [
74+
'command' => 'say Test',
75+
]);
76+
77+
$response->assertStatus(Response::HTTP_NO_CONTENT);
78+
}
79+
80+
/**
81+
* Test that an error is returned when the server is offline that is more specific than the
82+
* regular daemon connection error.
83+
*/
84+
public function testErrorIsReturnedWhenServerIsOffline()
85+
{
86+
[$user, $server] = $this->generateTestAccount();
87+
88+
$this->repository->expects('setServer->send')->andThrows(
89+
new BadResponseException('', new Request('GET', 'test'), new GuzzleResponse(Response::HTTP_BAD_GATEWAY))
90+
);
91+
92+
$response = $this->actingAs($user)->postJson("/api/client/servers/{$server->uuid}/command", [
93+
'command' => 'say Test',
94+
]);
95+
96+
$response->assertStatus(Response::HTTP_BAD_GATEWAY);
97+
$response->assertJsonPath('errors.0.code', 'HttpException');
98+
$response->assertJsonPath('errors.0.detail', 'Server must be online in order to send commands.');
99+
}
100+
}

tests/Integration/Api/Client/TwoFactorControllerTest.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,9 @@
66
use Pterodactyl\Models\User;
77
use Illuminate\Http\Response;
88
use PragmaRX\Google2FA\Google2FA;
9-
use Pterodactyl\Tests\Integration\IntegrationTestCase;
109

11-
class TwoFactorControllerTest extends IntegrationTestCase
10+
class TwoFactorControllerTest extends ClientApiIntegrationTestCase
1211
{
13-
/**
14-
* Clean up after tests have run.
15-
*/
16-
protected function tearDown(): void
17-
{
18-
User::query()->forceDelete();
19-
20-
parent::tearDown();
21-
}
22-
2312
/**
2413
* Test that image data for enabling 2FA is returned by the endpoint and that the user
2514
* record in the database is updated as expected.

0 commit comments

Comments
 (0)