Path: blob/1.0-develop/tests/Integration/Services/Deployment/FindViableNodesServiceTest.php
7460 views
<?php12namespace Pterodactyl\Tests\Integration\Services\Deployment;34use Pterodactyl\Models\Node;5use Pterodactyl\Models\Server;6use Pterodactyl\Models\Database;7use Pterodactyl\Models\Location;8use Illuminate\Support\Collection;9use Pterodactyl\Tests\Integration\IntegrationTestCase;10use Pterodactyl\Services\Deployment\FindViableNodesService;11use Pterodactyl\Exceptions\Service\Deployment\NoViableNodeException;1213class FindViableNodesServiceTest extends IntegrationTestCase14{15public function setUp(): void16{17parent::setUp();1819Database::query()->delete();20Server::query()->delete();21Node::query()->delete();22}2324public function testExceptionIsThrownIfNoDiskSpaceHasBeenSet()25{26$this->expectException(\InvalidArgumentException::class);27$this->expectExceptionMessage('Disk space must be an int, got NULL');2829$this->getService()->handle();30}3132public function testExceptionIsThrownIfNoMemoryHasBeenSet()33{34$this->expectException(\InvalidArgumentException::class);35$this->expectExceptionMessage('Memory usage must be an int, got NULL');3637$this->getService()->setDisk(10)->handle();38}3940/**41* Ensure that errors are not thrown back when passing in expected values.42*43* @see https://github.com/pterodactyl/panel/issues/252944*/45public function testNoExceptionIsThrownIfStringifiedIntegersArePassedForLocations()46{47$this->getService()->setLocations([1, 2, 3]);48$this->getService()->setLocations(['1', '2', '3']);49$this->getService()->setLocations(['1', 2, 3]);5051try {52$this->getService()->setLocations(['a']);53$this->fail('This expectation should not be called.');54} catch (\Exception $exception) {55$this->assertInstanceOf(\InvalidArgumentException::class, $exception);56$this->assertSame('An array of location IDs should be provided when calling setLocations.', $exception->getMessage());57}5859try {60$this->getService()->setLocations(['1.2', '1', 2]);61$this->fail('This expectation should not be called.');62} catch (\Exception $exception) {63$this->assertInstanceOf(\InvalidArgumentException::class, $exception);64$this->assertSame('An array of location IDs should be provided when calling setLocations.', $exception->getMessage());65}66}6768public function testExpectedNodeIsReturnedForLocation()69{70/** @var \Pterodactyl\Models\Location[] $locations */71$locations = Location::factory()->times(2)->create();7273/** @var \Pterodactyl\Models\Node[] $nodes */74$nodes = [75// This node should never be returned once we've completed the initial test which76// runs without a location filter.77Node::factory()->create([78'location_id' => $locations[0]->id,79'memory' => 2048,80'disk' => 1024 * 100,81]),82Node::factory()->create([83'location_id' => $locations[1]->id,84'memory' => 1024,85'disk' => 10240,86'disk_overallocate' => 10,87]),88Node::factory()->create([89'location_id' => $locations[1]->id,90'memory' => 1024 * 4,91'memory_overallocate' => 50,92'disk' => 102400,93]),94];9596// Expect that all the nodes are returned as we're under all of their limits97// and there is no location filter being provided.98$response = $this->getService()->setDisk(512)->setMemory(512)->handle();99$this->assertInstanceOf(Collection::class, $response);100$this->assertCount(3, $response);101$this->assertInstanceOf(Node::class, $response[0]);102103// Expect that only the last node is returned because it is the only one with enough104// memory available to this instance.105$response = $this->getService()->setDisk(512)->setMemory(2049)->handle();106$this->assertInstanceOf(Collection::class, $response);107$this->assertCount(1, $response);108$this->assertSame($nodes[2]->id, $response[0]->id);109110// Helper, I am lazy.111$base = function () use ($locations) {112return $this->getService()->setLocations([$locations[1]->id])->setDisk(512);113};114115// Expect that we can create this server on either node since the disk and memory116// limits are below the allowed amount.117$response = $base()->setMemory(512)->handle();118$this->assertCount(2, $response);119$this->assertSame(2, $response->where('location_id', $locations[1]->id)->count());120121// Expect that we can only create this server on the second node since the memory122// allocated is over the amount of memory available to the first node.123$response = $base()->setMemory(2048)->handle();124$this->assertCount(1, $response);125$this->assertSame($nodes[2]->id, $response[0]->id);126127// Expect that we can only create this server on the second node since the disk128// allocated is over the limit assigned to the first node (even with the overallocate).129$response = $base()->setDisk(20480)->setMemory(256)->handle();130$this->assertCount(1, $response);131$this->assertSame($nodes[2]->id, $response[0]->id);132133// Expect that we could create the server on either node since the disk allocated is134// right at the limit for Node 1 when the overallocate value is included in the calc.135$response = $base()->setDisk(11264)->setMemory(256)->handle();136$this->assertCount(2, $response);137138// Create two servers on the first node so that the disk space used is equal to the139// base amount available to the node (without overallocation included).140$servers = Collection::make([141$this->createServerModel(['node_id' => $nodes[1]->id, 'disk' => 5120]),142$this->createServerModel(['node_id' => $nodes[1]->id, 'disk' => 5120]),143]);144145// Expect that we cannot create a server with a 1GB disk on the first node since there146// is not enough space (even with the overallocate) available to the node.147$response = $base()->setDisk(1024)->setMemory(256)->handle();148$this->assertCount(1, $response);149$this->assertSame($nodes[2]->id, $response[0]->id);150151// Cleanup servers since we need to test some other stuff with memory here.152$servers->each->delete();153154// Expect that no viable node can be found when the memory limit for the given instance155// is greater than either node can support, even with the overallocation limits taken156// into account.157$this->expectException(NoViableNodeException::class);158$base()->setMemory(10000)->handle();159160// Create four servers so that the memory used for the second node is equal to the total161// limit for that node (pre-overallocate calculation).162Collection::make([163$this->createServerModel(['node_id' => $nodes[2]->id, 'memory' => 1024]),164$this->createServerModel(['node_id' => $nodes[2]->id, 'memory' => 1024]),165$this->createServerModel(['node_id' => $nodes[2]->id, 'memory' => 1024]),166$this->createServerModel(['node_id' => $nodes[2]->id, 'memory' => 1024]),167]);168169// Expect that either node can support this server when we account for the overallocate170// value of the second node.171$response = $base()->setMemory(500)->handle();172$this->assertCount(2, $response);173$this->assertSame(2, $response->where('location_id', $locations[1]->id)->count());174175// Expect that only the first node can support this server when we go over the remaining176// memory for the second nodes overallocate calculation.177$response = $base()->setMemory(640)->handle();178$this->assertCount(1, $response);179$this->assertSame($nodes[1]->id, $response[0]->id);180}181182private function getService(): FindViableNodesService183{184return $this->app->make(FindViableNodesService::class);185}186}187188189