Skip to content

Commit fc261fe

Browse files
committed
Add test cases for client servers endpoint
1 parent 4a677ae commit fc261fe

File tree

5 files changed

+178
-37
lines changed

5 files changed

+178
-37
lines changed

app/Models/Location.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
namespace Pterodactyl\Models;
44

5+
/**
6+
* @property int $id
7+
* @property string $short
8+
* @property string $long
9+
* @property \Carbon\Carbon $created_at
10+
* @property \Carbon\Carbon $updated_at
11+
*
12+
* @property \Pterodactyl\Models\Node[] $nodes
13+
* @property \Pterodactyl\Models\Server[] $servers
14+
*/
515
class Location extends Model
616
{
717
/**

database/factories/ModelFactory.php

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020

2121
$factory->define(Pterodactyl\Models\Server::class, function (Faker $faker) {
2222
return [
23-
'id' => $faker->unique()->randomNumber(),
24-
'node_id' => $faker->randomNumber(),
2523
'uuid' => $faker->unique()->uuid,
2624
'uuidShort' => str_random(8),
2725
'name' => $faker->firstName,
@@ -34,9 +32,6 @@
3432
'io' => 500,
3533
'cpu' => 0,
3634
'oom_disabled' => 0,
37-
'allocation_id' => $faker->randomNumber(),
38-
'nest_id' => $faker->randomNumber(),
39-
'egg_id' => $faker->randomNumber(),
4035
'pack_id' => null,
4136
'installed' => 1,
4237
'database_limit' => null,
@@ -50,7 +45,6 @@
5045
static $password;
5146

5247
return [
53-
'id' => $faker->unique()->randomNumber(),
5448
'external_id' => $faker->unique()->isbn10,
5549
'uuid' => $faker->uuid,
5650
'username' => $faker->userName,
@@ -74,15 +68,13 @@
7468

7569
$factory->define(Pterodactyl\Models\Location::class, function (Faker $faker) {
7670
return [
77-
'id' => $faker->unique()->randomNumber(),
78-
'short' => $faker->unique()->domainWord,
71+
'short' => Str::random(8),
7972
'long' => $faker->catchPhrase,
8073
];
8174
});
8275

8376
$factory->define(Pterodactyl\Models\Node::class, function (Faker $faker) {
8477
return [
85-
'id' => $faker->unique()->randomNumber(),
8678
'uuid' => Uuid::uuid4()->toString(),
8779
'public' => true,
8880
'name' => $faker->firstName,
@@ -104,7 +96,6 @@
10496

10597
$factory->define(Pterodactyl\Models\Nest::class, function (Faker $faker) {
10698
return [
107-
'id' => $faker->unique()->randomNumber(),
10899
'uuid' => $faker->unique()->uuid,
109100
'author' => 'testauthor@example.com',
110101
'name' => $faker->word,
@@ -114,9 +105,7 @@
114105

115106
$factory->define(Pterodactyl\Models\Egg::class, function (Faker $faker) {
116107
return [
117-
'id' => $faker->unique()->randomNumber(),
118108
'uuid' => $faker->unique()->uuid,
119-
'nest_id' => $faker->unique()->randomNumber(),
120109
'name' => $faker->name,
121110
'description' => implode(' ', $faker->sentences(3)),
122111
'startup' => 'java -jar test.jar',
@@ -125,7 +114,6 @@
125114

126115
$factory->define(Pterodactyl\Models\EggVariable::class, function (Faker $faker) {
127116
return [
128-
'id' => $faker->unique()->randomNumber(),
129117
'name' => $faker->firstName,
130118
'description' => $faker->sentence(),
131119
'env_variable' => strtoupper(str_replace(' ', '_', $faker->words(2, true))),
@@ -146,8 +134,6 @@
146134

147135
$factory->define(Pterodactyl\Models\Pack::class, function (Faker $faker) {
148136
return [
149-
'id' => $faker->unique()->randomNumber(),
150-
'egg_id' => $faker->randomNumber(),
151137
'uuid' => $faker->uuid,
152138
'name' => $faker->word,
153139
'description' => null,
@@ -159,41 +145,30 @@
159145
});
160146

161147
$factory->define(Pterodactyl\Models\Subuser::class, function (Faker $faker) {
162-
return [
163-
'id' => $faker->unique()->randomNumber(),
164-
'user_id' => $faker->randomNumber(),
165-
'server_id' => $faker->randomNumber(),
166-
];
148+
return [];
167149
});
168150

169151
$factory->define(Pterodactyl\Models\Allocation::class, function (Faker $faker) {
170152
return [
171-
'id' => $faker->unique()->randomNumber(),
172-
'node_id' => $faker->randomNumber(),
173153
'ip' => $faker->ipv4,
174154
'port' => $faker->randomNumber(5),
175155
];
176156
});
177157

178158
$factory->define(Pterodactyl\Models\DatabaseHost::class, function (Faker $faker) {
179159
return [
180-
'id' => $faker->unique()->randomNumber(),
181160
'name' => $faker->colorName,
182161
'host' => $faker->unique()->ipv4,
183162
'port' => 3306,
184163
'username' => $faker->colorName,
185164
'password' => Crypt::encrypt($faker->word),
186-
'node_id' => $faker->randomNumber(),
187165
];
188166
});
189167

190168
$factory->define(Pterodactyl\Models\Database::class, function (Faker $faker) {
191169
static $password;
192170

193171
return [
194-
'id' => $faker->unique()->randomNumber(),
195-
'server_id' => $faker->randomNumber(),
196-
'database_host_id' => $faker->randomNumber(),
197172
'database' => str_random(10),
198173
'username' => str_random(10),
199174
'remote' => '%',
@@ -205,16 +180,12 @@
205180

206181
$factory->define(Pterodactyl\Models\Schedule::class, function (Faker $faker) {
207182
return [
208-
'id' => $faker->unique()->randomNumber(),
209-
'server_id' => $faker->randomNumber(),
210183
'name' => $faker->firstName(),
211184
];
212185
});
213186

214187
$factory->define(Pterodactyl\Models\Task::class, function (Faker $faker) {
215188
return [
216-
'id' => $faker->unique()->randomNumber(),
217-
'schedule_id' => $faker->randomNumber(),
218189
'sequence_id' => $faker->randomNumber(1),
219190
'action' => 'command',
220191
'payload' => 'test command',
@@ -225,9 +196,6 @@
225196

226197
$factory->define(Pterodactyl\Models\DaemonKey::class, function (Faker $faker) {
227198
return [
228-
'id' => $faker->unique()->randomNumber(),
229-
'server_id' => $faker->randomNumber(),
230-
'user_id' => $faker->randomNumber(),
231199
'secret' => 'i_' . str_random(40),
232200
'expires_at' => \Carbon\Carbon::now()->addMinutes(10)->toDateTimeString(),
233201
];
@@ -237,8 +205,6 @@
237205
static $token;
238206

239207
return [
240-
'id' => $faker->unique()->randomNumber(),
241-
'user_id' => $faker->randomNumber(),
242208
'key_type' => ApiKey::TYPE_APPLICATION,
243209
'identifier' => str_random(Pterodactyl\Models\ApiKey::IDENTIFIER_LENGTH),
244210
'token' => $token ?: $token = encrypt(str_random(Pterodactyl\Models\ApiKey::KEY_LENGTH)),

tests/Integration/Api/Client/ApiKeyControllerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public function testApiKeyCanBeCreatedForAccount()
6363
/** @var \Pterodactyl\Models\User $user */
6464
$user = factory(User::class)->create();
6565

66-
// Small sub-test to ensure we're always comparing the number of keys to the
66+
// Small sub-test to ensure we're always comparing the number of keys to the
6767
// specific logged in account, and not just the total number of keys stored in
6868
// the database.
6969
factory(ApiKey::class)->times(10)->create([
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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\Models\Permission;
11+
use Pterodactyl\Tests\Integration\IntegrationTestCase;
12+
13+
class ClientControllerTest extends IntegrationTestCase
14+
{
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+
28+
/**
29+
* Test that only the servers a logged in user is assigned to are returned by the
30+
* API endpoint. Obviously there are cases such as being an administrator or being
31+
* a subuser, but for this test we just want to test a basic scenario and pretend
32+
* subusers do not exist at all.
33+
*/
34+
public function testOnlyLoggedInUsersServersAreReturned()
35+
{
36+
/** @var \Pterodactyl\Models\User[] $users */
37+
$users = factory(User::class)->times(3)->create();
38+
39+
/** @var \Pterodactyl\Models\Server[] $servers */
40+
$servers = [
41+
$this->createServerModel(['user_id' => $users[0]->id]),
42+
$this->createServerModel(['user_id' => $users[1]->id]),
43+
$this->createServerModel(['user_id' => $users[2]->id]),
44+
];
45+
46+
$response = $this->actingAs($users[0])->getJson('/api/client');
47+
48+
$response->assertOk();
49+
$response->assertJsonPath('object', 'list');
50+
$response->assertJsonPath('data.0.object', Server::RESOURCE_NAME);
51+
$response->assertJsonPath('data.0.attributes.identifier', $servers[0]->uuidShort);
52+
$response->assertJsonPath('data.0.attributes.server_owner', true);
53+
$response->assertJsonPath('meta.pagination.total', 1);
54+
$response->assertJsonPath('meta.pagination.per_page', config('pterodactyl.paginate.frontend.servers'));
55+
}
56+
57+
/**
58+
* Tests that all of the servers on the system are returned when making the request as an
59+
* administrator and including the ?filter=all parameter in the URL.
60+
*/
61+
public function testFilterIncludeAllServersWhenAdministrator()
62+
{
63+
/** @var \Pterodactyl\Models\User[] $users */
64+
$users = factory(User::class)->times(3)->create();
65+
$users[0]->root_admin = true;
66+
67+
$servers = [
68+
$this->createServerModel(['user_id' => $users[0]->id]),
69+
$this->createServerModel(['user_id' => $users[1]->id]),
70+
$this->createServerModel(['user_id' => $users[2]->id]),
71+
];
72+
73+
$response = $this->actingAs($users[0])->getJson('/api/client?filter=all');
74+
75+
$response->assertOk();
76+
$response->assertJsonCount(3, 'data');
77+
78+
for ($i = 0; $i < 3; $i++) {
79+
$response->assertJsonPath("data.{$i}.attributes.server_owner", $i === 0);
80+
$response->assertJsonPath("data.{$i}.attributes.identifier", $servers[$i]->uuidShort);
81+
}
82+
}
83+
84+
/**
85+
* Test that servers where the user is a subuser are returned by default in the API call.
86+
*/
87+
public function testServersUserIsASubuserOfAreReturned()
88+
{
89+
/** @var \Pterodactyl\Models\User[] $users */
90+
$users = factory(User::class)->times(3)->create();
91+
$servers = [
92+
$this->createServerModel(['user_id' => $users[0]->id]),
93+
$this->createServerModel(['user_id' => $users[1]->id]),
94+
$this->createServerModel(['user_id' => $users[2]->id]),
95+
];
96+
97+
// Set user 0 as a subuser of server 1. Thus, we should get two servers
98+
// back in the response when making the API call as user 0.
99+
Subuser::query()->create([
100+
'user_id' => $users[0]->id,
101+
'server_id' => $servers[1]->id,
102+
'permissions' => [Permission::ACTION_WEBSOCKET_CONNECT],
103+
]);
104+
105+
$response = $this->actingAs($users[0])->getJson('/api/client');
106+
107+
$response->assertOk();
108+
$response->assertJsonCount(2, 'data');
109+
$response->assertJsonPath('data.0.attributes.server_owner', true);
110+
$response->assertJsonPath('data.0.attributes.identifier', $servers[0]->uuidShort);
111+
$response->assertJsonPath('data.1.attributes.server_owner', false);
112+
$response->assertJsonPath('data.1.attributes.identifier', $servers[1]->uuidShort);
113+
}
114+
115+
/**
116+
* Returns only servers that the user owns, not servers they are a subuser of.
117+
*/
118+
public function testFilterOnlyOwnerServers()
119+
{
120+
/** @var \Pterodactyl\Models\User[] $users */
121+
$users = factory(User::class)->times(3)->create();
122+
$servers = [
123+
$this->createServerModel(['user_id' => $users[0]->id]),
124+
$this->createServerModel(['user_id' => $users[1]->id]),
125+
$this->createServerModel(['user_id' => $users[2]->id]),
126+
];
127+
128+
// Set user 0 as a subuser of server 1. Thus, we should get two servers
129+
// back in the response when making the API call as user 0.
130+
Subuser::query()->create([
131+
'user_id' => $users[0]->id,
132+
'server_id' => $servers[1]->id,
133+
'permissions' => [Permission::ACTION_WEBSOCKET_CONNECT],
134+
]);
135+
136+
$response = $this->actingAs($users[0])->getJson('/api/client?filter=owner');
137+
138+
$response->assertOk();
139+
$response->assertJsonCount(1, 'data');
140+
$response->assertJsonPath('data.0.attributes.server_owner', true);
141+
$response->assertJsonPath('data.0.attributes.identifier', $servers[0]->uuidShort);
142+
}
143+
144+
/**
145+
* Tests that the permissions from the Panel are returned correctly.
146+
*/
147+
public function testPermissionsAreReturned()
148+
{
149+
/** @var \Pterodactyl\Models\User $user */
150+
$user = factory(User::class)->create();
151+
152+
$this->actingAs($user)
153+
->getJson('/api/client/permissions')
154+
->assertOk()
155+
->assertJson([
156+
'object' => 'system_permissions',
157+
'attributes' => [
158+
'permissions' => Permission::permissions()->toArray(),
159+
],
160+
]);
161+
}
162+
}

tests/Integration/IntegrationTestCase.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
use Tests\TestCase;
66
use Cake\Chronos\Chronos;
77
use Illuminate\Database\Eloquent\Model;
8+
use Tests\Traits\Integration\CreatesTestModels;
89
use Pterodactyl\Transformers\Api\Application\BaseTransformer;
910

1011
abstract class IntegrationTestCase extends TestCase
1112
{
13+
use CreatesTestModels;
14+
1215
/**
1316
* Setup base integration test cases.
1417
*/

0 commit comments

Comments
 (0)