Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pterodactyl
GitHub Repository: pterodactyl/panel
Path: blob/1.0-develop/tests/Integration/Services/Servers/BuildModificationServiceTest.php
7460 views
1
<?php
2
3
namespace Pterodactyl\Tests\Integration\Services\Servers;
4
5
use Mockery\MockInterface;
6
use GuzzleHttp\Psr7\Request;
7
use GuzzleHttp\Psr7\Response;
8
use Pterodactyl\Models\Server;
9
use Pterodactyl\Models\Allocation;
10
use GuzzleHttp\Exception\RequestException;
11
use Pterodactyl\Exceptions\DisplayException;
12
use Pterodactyl\Tests\Integration\IntegrationTestCase;
13
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
14
use Pterodactyl\Services\Servers\BuildModificationService;
15
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
16
17
class BuildModificationServiceTest extends IntegrationTestCase
18
{
19
private MockInterface $daemonServerRepository;
20
21
/**
22
* Setup tests.
23
*/
24
public function setUp(): void
25
{
26
parent::setUp();
27
28
$this->daemonServerRepository = $this->mock(DaemonServerRepository::class);
29
}
30
31
/**
32
* Test that allocations can be added and removed from a server. Only the allocations on the
33
* current node and belonging to this server should be modified.
34
*/
35
public function testAllocationsCanBeModifiedForTheServer()
36
{
37
$server = $this->createServerModel();
38
$server2 = $this->createServerModel();
39
40
/** @var \Pterodactyl\Models\Allocation[] $allocations */
41
$allocations = Allocation::factory()->times(4)->create(['node_id' => $server->node_id, 'notes' => 'Random notes']);
42
43
$initialAllocationId = $server->allocation_id;
44
$allocations[0]->update(['server_id' => $server->id, 'notes' => 'Test notes']);
45
46
// Some additional test allocations for the other server, not the server we are attempting
47
// to modify.
48
$allocations[2]->update(['server_id' => $server2->id]);
49
$allocations[3]->update(['server_id' => $server2->id]);
50
51
$this->daemonServerRepository->expects('setServer->sync')->andReturnUndefined();
52
53
$response = $this->getService()->handle($server, [
54
// Attempt to add one new allocation, and an allocation assigned to another server. The
55
// other server allocation should be ignored, and only the allocation for this server should
56
// be used.
57
'add_allocations' => [$allocations[2]->id, $allocations[1]->id],
58
// Remove the default server allocation, ensuring that the new allocation passed through
59
// in the data becomes the default allocation.
60
'remove_allocations' => [$server->allocation_id, $allocations[0]->id, $allocations[3]->id],
61
]);
62
63
$this->assertInstanceOf(Server::class, $response);
64
65
// Only one allocation should exist for this server now.
66
$this->assertCount(1, $response->allocations);
67
$this->assertSame($allocations[1]->id, $response->allocation_id);
68
$this->assertNull($response->allocation->notes);
69
70
// These two allocations should not have been touched.
71
$this->assertDatabaseHas('allocations', ['id' => $allocations[2]->id, 'server_id' => $server2->id]);
72
$this->assertDatabaseHas('allocations', ['id' => $allocations[3]->id, 'server_id' => $server2->id]);
73
74
// Both of these allocations should have been removed from the server, and have had their
75
// notes properly reset.
76
$this->assertDatabaseHas('allocations', ['id' => $initialAllocationId, 'server_id' => null, 'notes' => null]);
77
$this->assertDatabaseHas('allocations', ['id' => $allocations[0]->id, 'server_id' => null, 'notes' => null]);
78
}
79
80
/**
81
* Test that an exception is thrown if removing the default allocation without also assigning
82
* new allocations to the server.
83
*/
84
public function testExceptionIsThrownIfRemovingTheDefaultAllocation()
85
{
86
$server = $this->createServerModel();
87
/** @var \Pterodactyl\Models\Allocation[] $allocations */
88
$allocations = Allocation::factory()->times(4)->create(['node_id' => $server->node_id]);
89
90
$allocations[0]->update(['server_id' => $server->id]);
91
92
$this->expectException(DisplayException::class);
93
$this->expectExceptionMessage('You are attempting to delete the default allocation for this server but there is no fallback allocation to use.');
94
95
$this->getService()->handle($server, [
96
'add_allocations' => [],
97
'remove_allocations' => [$server->allocation_id, $allocations[0]->id],
98
]);
99
}
100
101
/**
102
* Test that the build data for the server is properly passed along to the Wings instance so that
103
* the server data is updated in realtime. This test also ensures that only certain fields get updated
104
* for the server, and not just any arbitrary field.
105
*/
106
public function testServerBuildDataIsProperlyUpdatedOnWings()
107
{
108
$server = $this->createServerModel();
109
110
$this->daemonServerRepository->expects('setServer')->with(\Mockery::on(function (Server $s) use ($server) {
111
return $s->id === $server->id;
112
}))->andReturnSelf();
113
114
$this->daemonServerRepository->expects('sync')->withNoArgs()->andReturnUndefined();
115
116
$response = $this->getService()->handle($server, [
117
'oom_disabled' => false,
118
'memory' => 256,
119
'swap' => 128,
120
'io' => 600,
121
'cpu' => 150,
122
'threads' => '1,2',
123
'disk' => 1024,
124
'backup_limit' => null,
125
'database_limit' => 10,
126
'allocation_limit' => 20,
127
]);
128
129
$this->assertFalse($response->oom_disabled);
130
$this->assertSame(256, $response->memory);
131
$this->assertSame(128, $response->swap);
132
$this->assertSame(600, $response->io);
133
$this->assertSame(150, $response->cpu);
134
$this->assertSame('1,2', $response->threads);
135
$this->assertSame(1024, $response->disk);
136
$this->assertSame(0, $response->backup_limit);
137
$this->assertSame(10, $response->database_limit);
138
$this->assertSame(20, $response->allocation_limit);
139
}
140
141
/**
142
* Test that an exception when connecting to the Wings instance is properly ignored
143
* when making updates. This allows for a server to be modified even when the Wings
144
* node is offline.
145
*/
146
public function testConnectionExceptionIsIgnoredWhenUpdatingServerSettings()
147
{
148
$server = $this->createServerModel();
149
150
$this->daemonServerRepository->expects('setServer->sync')->andThrows(
151
new DaemonConnectionException(
152
new RequestException('Bad request', new Request('GET', '/test'), new Response())
153
)
154
);
155
156
$response = $this->getService()->handle($server, ['memory' => 256, 'disk' => 10240]);
157
158
$this->assertInstanceOf(Server::class, $response);
159
$this->assertSame(256, $response->memory);
160
$this->assertSame(10240, $response->disk);
161
162
$this->assertDatabaseHas('servers', ['id' => $response->id, 'memory' => 256, 'disk' => 10240]);
163
}
164
165
/**
166
* Test that no exception is thrown if we are only removing an allocation.
167
*/
168
public function testNoExceptionIsThrownIfOnlyRemovingAllocation()
169
{
170
$server = $this->createServerModel();
171
/** @var Allocation $allocation */
172
$allocation = Allocation::factory()->create(['node_id' => $server->node_id, 'server_id' => $server->id]);
173
174
$this->daemonServerRepository->expects('setServer->sync')->andReturnUndefined();
175
176
$this->getService()->handle($server, [
177
'remove_allocations' => [$allocation->id],
178
]);
179
180
$this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]);
181
}
182
183
/**
184
* Test that allocations in both the add and remove arrays are only added, and not removed.
185
* This scenario wouldn't really happen in the UI, but it is possible to perform via the API,
186
* so we want to make sure that the logic being used doesn't break if the allocation exists
187
* in both arrays.
188
*
189
* We'll default to adding the allocation in this case.
190
*/
191
public function testAllocationInBothAddAndRemoveIsAdded()
192
{
193
$server = $this->createServerModel();
194
/** @var Allocation $allocation */
195
$allocation = Allocation::factory()->create(['node_id' => $server->node_id]);
196
197
$this->daemonServerRepository->expects('setServer->sync')->andReturnUndefined();
198
199
$this->getService()->handle($server, [
200
'add_allocations' => [$allocation->id],
201
'remove_allocations' => [$allocation->id],
202
]);
203
204
$this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => $server->id]);
205
}
206
207
/**
208
* Test that using the same allocation ID multiple times in the array does not cause an error.
209
*/
210
public function testUsingSameAllocationIdMultipleTimesDoesNotError()
211
{
212
$server = $this->createServerModel();
213
/** @var Allocation $allocation */
214
$allocation = Allocation::factory()->create(['node_id' => $server->node_id, 'server_id' => $server->id]);
215
/** @var Allocation $allocation2 */
216
$allocation2 = Allocation::factory()->create(['node_id' => $server->node_id]);
217
218
$this->daemonServerRepository->expects('setServer->sync')->andReturnUndefined();
219
220
$this->getService()->handle($server, [
221
'add_allocations' => [$allocation2->id, $allocation2->id],
222
'remove_allocations' => [$allocation->id, $allocation->id],
223
]);
224
225
$this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]);
226
$this->assertDatabaseHas('allocations', ['id' => $allocation2->id, 'server_id' => $server->id]);
227
}
228
229
/**
230
* Test that any changes we made to the server or allocations are rolled back if there is an
231
* exception while performing any action. This is different from the connection exception
232
* test which should properly ignore connection issues. We want any other type of exception
233
* to properly be thrown back to the caller.
234
*/
235
public function testThatUpdatesAreRolledBackIfExceptionIsEncountered()
236
{
237
$server = $this->createServerModel();
238
/** @var Allocation $allocation */
239
$allocation = Allocation::factory()->create(['node_id' => $server->node_id]);
240
241
$this->daemonServerRepository->expects('setServer->sync')->andThrows(new DisplayException('Test'));
242
243
$this->expectException(DisplayException::class);
244
245
$this->getService()->handle($server, ['add_allocations' => [$allocation->id]]);
246
247
$this->assertDatabaseHas('allocations', ['id' => $allocation->id, 'server_id' => null]);
248
}
249
250
private function getService(): BuildModificationService
251
{
252
return $this->app->make(BuildModificationService::class);
253
}
254
}
255
256