Skip to content

Commit 6cb21fb

Browse files
committed
Add test coverage for allocation auto-assignment service
1 parent d493685 commit 6cb21fb

File tree

2 files changed

+185
-3
lines changed

2 files changed

+185
-3
lines changed

app/Services/Allocations/FindAssignableAllocationService.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use Webmozart\Assert\Assert;
66
use Pterodactyl\Models\Server;
77
use Pterodactyl\Models\Allocation;
8-
use Pterodactyl\Exceptions\DisplayException;
98
use Pterodactyl\Exceptions\Service\Allocation\AutoAllocationNotEnabledException;
109
use Pterodactyl\Exceptions\Service\Allocation\NoAutoAllocationSpaceAvailableException;
1110

@@ -42,7 +41,7 @@ public function __construct(AssignmentService $service)
4241
*/
4342
public function handle(Server $server)
4443
{
45-
if (!config('pterodactyl.client_features.allocations.enabled')) {
44+
if (! config('pterodactyl.client_features.allocations.enabled')) {
4645
throw new AutoAllocationNotEnabledException;
4746
}
4847

@@ -64,6 +63,10 @@ public function handle(Server $server)
6463
}
6564

6665
/**
66+
* Create a new allocation on the server's node with a random port from the defined range
67+
* in the settings. If there are no matches in that range, or something is wrong with the
68+
* range information provided an exception will be raised.
69+
*
6770
* @param \Pterodactyl\Models\Server $server
6871
* @return \Pterodactyl\Models\Allocation
6972
*
@@ -78,7 +81,7 @@ protected function createNewAllocation(Server $server): Allocation
7881
$start = config('pterodactyl.client_features.allocations.range_start', null);
7982
$end = config('pterodactyl.client_features.allocations.range_end', null);
8083

81-
if (!$start || !$end) {
84+
if (! $start || ! $end) {
8285
throw new NoAutoAllocationSpaceAvailableException;
8386
}
8487

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
<?php
2+
3+
namespace Pterodactyl\Tests\Integration\Services\Allocations;
4+
5+
use Exception;
6+
use InvalidArgumentException;
7+
use Pterodactyl\Models\Allocation;
8+
use Pterodactyl\Tests\Integration\IntegrationTestCase;
9+
use Pterodactyl\Services\Allocations\FindAssignableAllocationService;
10+
use Pterodactyl\Exceptions\Service\Allocation\AutoAllocationNotEnabledException;
11+
use Pterodactyl\Exceptions\Service\Allocation\NoAutoAllocationSpaceAvailableException;
12+
13+
class FindAssignableAllocationServiceTest extends IntegrationTestCase
14+
{
15+
/**
16+
* Setup tests.
17+
*/
18+
public function setUp(): void
19+
{
20+
parent::setUp();
21+
22+
config()->set('pterodactyl.client_features.allocations.enabled', true);
23+
config()->set('pterodactyl.client_features.allocations.range_start', 0);
24+
config()->set('pterodactyl.client_features.allocations.range_end', 0);
25+
}
26+
27+
/**
28+
* Test that an unassigned allocation is prefered rather than creating an entirely new
29+
* allocation for the server.
30+
*/
31+
public function testExistingAllocationIsPreferred()
32+
{
33+
$server = $this->createServerModel();
34+
35+
$created = factory(Allocation::class)->create([
36+
'node_id' => $server->node_id,
37+
'ip' => $server->allocation->ip,
38+
]);
39+
40+
$response = $this->getService()->handle($server);
41+
42+
$this->assertSame($created->id, $response->id);
43+
$this->assertSame($server->allocation->ip, $response->ip);
44+
$this->assertSame($server->node_id, $response->node_id);
45+
$this->assertSame($server->id, $response->server_id);
46+
$this->assertNotSame($server->allocation_id, $response->id);
47+
}
48+
49+
/**
50+
* Test that a new allocation is created if there is not a free one available.
51+
*/
52+
public function testNewAllocationIsCreatedIfOneIsNotFound()
53+
{
54+
$server = $this->createServerModel();
55+
config()->set('pterodactyl.client_features.allocations.range_start', 5000);
56+
config()->set('pterodactyl.client_features.allocations.range_end', 5005);
57+
58+
$response = $this->getService()->handle($server);
59+
$this->assertSame($server->id, $response->server_id);
60+
$this->assertSame($server->allocation->ip, $response->ip);
61+
$this->assertSame($server->node_id, $response->node_id);
62+
$this->assertNotSame($server->allocation_id, $response->id);
63+
$this->assertTrue($response->port >= 5000 && $response->port <= 5005);
64+
}
65+
66+
/**
67+
* Test that a currently assigned port is never assigned to a server.
68+
*/
69+
public function testOnlyPortNotInUseIsCreated()
70+
{
71+
$server = $this->createServerModel();
72+
$server2 = $this->createServerModel(['node_id' => $server->node_id]);
73+
74+
config()->set('pterodactyl.client_features.allocations.range_start', 5000);
75+
config()->set('pterodactyl.client_features.allocations.range_end', 5001);
76+
77+
factory(Allocation::class)->create([
78+
'server_id' => $server2->id,
79+
'node_id' => $server->node_id,
80+
'ip' => $server->allocation->ip,
81+
'port' => 5000,
82+
]);
83+
84+
$response = $this->getService()->handle($server);
85+
$this->assertSame(5001, $response->port);
86+
}
87+
88+
public function testExceptionIsThrownIfNoMoreAllocationsCanBeCreatedInRange()
89+
{
90+
$server = $this->createServerModel();
91+
$server2 = $this->createServerModel(['node_id' => $server->node_id]);
92+
config()->set('pterodactyl.client_features.allocations.range_start', 5000);
93+
config()->set('pterodactyl.client_features.allocations.range_end', 5005);
94+
95+
for ($i = 5000; $i <= 5005; $i++) {
96+
factory(Allocation::class)->create([
97+
'ip' => $server->allocation->ip,
98+
'port' => $i,
99+
'node_id' => $server->node_id,
100+
'server_id' => $server2->id,
101+
]);
102+
}
103+
104+
$this->expectException(NoAutoAllocationSpaceAvailableException::class);
105+
$this->expectExceptionMessage('Cannot assign additional allocation: no more space available on node.');
106+
107+
$this->getService()->handle($server);
108+
}
109+
110+
/**
111+
* Test that we only auto-allocate from the current server's IP address space, and not a random
112+
* IP address available on that node.
113+
*/
114+
public function testExceptionIsThrownIfOnlyFreePortIsOnADifferentIp()
115+
{
116+
$server = $this->createServerModel();
117+
118+
factory(Allocation::class)->times(5)->create(['node_id' => $server->node_id]);
119+
120+
$this->expectException(NoAutoAllocationSpaceAvailableException::class);
121+
$this->expectExceptionMessage('Cannot assign additional allocation: no more space available on node.');
122+
123+
$this->getService()->handle($server);
124+
}
125+
126+
public function testExceptionIsThrownIfStartOrEndRangeIsNotDefined()
127+
{
128+
$server = $this->createServerModel();
129+
130+
$this->expectException(NoAutoAllocationSpaceAvailableException::class);
131+
$this->expectExceptionMessage('Cannot assign additional allocation: no more space available on node.');
132+
133+
$this->getService()->handle($server);
134+
}
135+
136+
public function testExceptionIsThrownIfStartOrEndRangeIsNotNumeric()
137+
{
138+
$server = $this->createServerModel();
139+
config()->set('pterodactyl.client_features.allocations.range_start', 'hodor');
140+
config()->set('pterodactyl.client_features.allocations.range_end', 10);
141+
142+
try {
143+
$this->getService()->handle($server);
144+
$this->assertTrue(false, 'This assertion should not be reached.');
145+
} catch (Exception $exception) {
146+
$this->assertInstanceOf(InvalidArgumentException::class, $exception);
147+
$this->assertSame('Expected an integerish value. Got: string', $exception->getMessage());
148+
}
149+
150+
config()->set('pterodactyl.client_features.allocations.range_start', 10);
151+
config()->set('pterodactyl.client_features.allocations.range_end', 'hodor');
152+
153+
try {
154+
$this->getService()->handle($server);
155+
$this->assertTrue(false, 'This assertion should not be reached.');
156+
} catch (Exception $exception) {
157+
$this->assertInstanceOf(InvalidArgumentException::class, $exception);
158+
$this->assertSame('Expected an integerish value. Got: string', $exception->getMessage());
159+
}
160+
}
161+
162+
public function testExceptionIsThrownIfFeatureIsNotEnabled()
163+
{
164+
config()->set('pterodactyl.client_features.allocations.enabled', false);
165+
$server = $this->createServerModel();
166+
167+
$this->expectException(AutoAllocationNotEnabledException::class);
168+
169+
$this->getService()->handle($server);
170+
}
171+
172+
/**
173+
* @return \Pterodactyl\Services\Allocations\FindAssignableAllocationService
174+
*/
175+
private function getService()
176+
{
177+
return $this->app->make(FindAssignableAllocationService::class);
178+
}
179+
}

0 commit comments

Comments
 (0)