Path: blob/1.0-develop/tests/Integration/Services/Databases/DatabaseManagementServiceTest.php
7460 views
<?php12namespace Pterodactyl\Tests\Integration\Services\Databases;34use Mockery\MockInterface;5use Pterodactyl\Models\Database;6use Pterodactyl\Models\DatabaseHost;7use Pterodactyl\Tests\Integration\IntegrationTestCase;8use Pterodactyl\Repositories\Eloquent\DatabaseRepository;9use Pterodactyl\Services\Databases\DatabaseManagementService;10use Pterodactyl\Exceptions\Repository\DuplicateDatabaseNameException;11use Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException;12use Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException;1314class DatabaseManagementServiceTest extends IntegrationTestCase15{16private MockInterface $repository;1718/**19* Setup tests.20*/21public function setUp(): void22{23parent::setUp();2425config()->set('pterodactyl.client_features.databases.enabled', true);2627$this->repository = $this->mock(DatabaseRepository::class);28}2930/**31* Test that the name generated by the unique name function is what we expect.32*/33public function testUniqueDatabaseNameIsGeneratedCorrectly()34{35$this->assertSame('s1_example', DatabaseManagementService::generateUniqueDatabaseName('example', 1));36$this->assertSame('s123_something_else', DatabaseManagementService::generateUniqueDatabaseName('something_else', 123));37$this->assertSame('s123_' . str_repeat('a', 43), DatabaseManagementService::generateUniqueDatabaseName(str_repeat('a', 100), 123));38}3940/**41* Test that disabling the client database feature flag prevents the creation of databases.42*/43public function testExceptionIsThrownIfClientDatabasesAreNotEnabled()44{45config()->set('pterodactyl.client_features.databases.enabled', false);4647$this->expectException(DatabaseClientFeatureNotEnabledException::class);4849$server = $this->createServerModel();50$this->getService()->create($server, []);51}5253/**54* Test that a server at its database limit cannot have an additional one created if55* the $validateDatabaseLimit flag is not set to false.56*/57public function testDatabaseCannotBeCreatedIfServerHasReachedLimit()58{59$server = $this->createServerModel(['database_limit' => 2]);60$host = DatabaseHost::factory()->create(['node_id' => $server->node_id]);6162Database::factory()->times(2)->create(['server_id' => $server->id, 'database_host_id' => $host->id]);6364$this->expectException(TooManyDatabasesException::class);6566$this->getService()->create($server, []);67}6869/**70* Test that a missing or invalid database name format causes an exception to be thrown.71*/72#[\PHPUnit\Framework\Attributes\DataProvider('invalidDataDataProvider')]73public function testEmptyDatabaseNameOrInvalidNameTriggersAnException(array $data)74{75$server = $this->createServerModel();7677$this->expectException(\InvalidArgumentException::class);78$this->expectExceptionMessage('The database name passed to DatabaseManagementService::handle MUST be prefixed with "s{server_id}_".');7980$this->getService()->create($server, $data);81}8283/**84* Test that creating a server database with an identical name triggers an exception.85*/86public function testCreatingDatabaseWithIdenticalNameTriggersAnException()87{88$server = $this->createServerModel();89$name = DatabaseManagementService::generateUniqueDatabaseName('soemthing', $server->id);9091$host = DatabaseHost::factory()->create(['node_id' => $server->node_id]);92$host2 = DatabaseHost::factory()->create(['node_id' => $server->node_id]);93Database::factory()->create([94'database' => $name,95'database_host_id' => $host->id,96'server_id' => $server->id,97]);9899$this->expectException(DuplicateDatabaseNameException::class);100$this->expectExceptionMessage('A database with that name already exists for this server.');101102// Try to create a database with the same name as a database on a different host. We expect103// this to fail since we don't account for the specific host when checking uniqueness.104$this->getService()->create($server, [105'database' => $name,106'database_host_id' => $host2->id,107]);108109$this->assertDatabaseMissing('databases', ['server_id' => $server->id]);110}111112/**113* Test that a server database can be created successfully.114*/115public function testServerDatabaseCanBeCreated()116{117$server = $this->createServerModel();118$name = DatabaseManagementService::generateUniqueDatabaseName('soemthing', $server->id);119120$host = DatabaseHost::factory()->create(['node_id' => $server->node_id]);121122$this->repository->expects('createDatabase')->with($name);123124$username = null;125$secondUsername = null;126$password = null;127128// The value setting inside the closures if to avoid throwing an exception during the129// assertions that would get caught by the functions catcher and thus lead to the exception130// being swallowed incorrectly.131$this->repository->expects('createUser')->with(132\Mockery::on(function ($value) use (&$username) {133$username = $value;134135return true;136}),137'%',138\Mockery::on(function ($value) use (&$password) {139$password = $value;140141return true;142}),143null144);145146$this->repository->expects('assignUserToDatabase')->with($name, \Mockery::on(function ($value) use (&$secondUsername) {147$secondUsername = $value;148149return true;150}), '%');151152$this->repository->expects('flush')->withNoArgs();153154$response = $this->getService()->create($server, [155'remote' => '%',156'database' => $name,157'database_host_id' => $host->id,158]);159160$this->assertInstanceOf(Database::class, $response);161$this->assertSame($response->server_id, $server->id);162$this->assertMatchesRegularExpression('/^(u\d+_)(\w){10}$/', $username);163$this->assertSame($username, $secondUsername);164$this->assertSame(24, strlen($password));165166$this->assertDatabaseHas('databases', ['server_id' => $server->id, 'id' => $response->id]);167}168169/**170* Test that an exception encountered while creating the database leads to the cleanup code171* being called and any exceptions encountered while cleaning up go unreported.172*/173public function testExceptionEncounteredWhileCreatingDatabaseAttemptsToCleanup()174{175$server = $this->createServerModel();176$name = DatabaseManagementService::generateUniqueDatabaseName('soemthing', $server->id);177178$host = DatabaseHost::factory()->create(['node_id' => $server->node_id]);179180$this->repository->expects('createDatabase')->with($name)->andThrows(new \BadMethodCallException());181$this->repository->expects('dropDatabase')->with($name);182$this->repository->expects('dropUser')->withAnyArgs()->andThrows(new \InvalidArgumentException());183184$this->expectException(\BadMethodCallException::class);185186$this->getService()->create($server, [187'remote' => '%',188'database' => $name,189'database_host_id' => $host->id,190]);191192$this->assertDatabaseMissing('databases', ['server_id' => $server->id]);193}194195public static function invalidDataDataProvider(): array196{197return [198[[]],199[['database' => '']],200[['database' => 'something']],201[['database' => 's_something']],202[['database' => 's12s_something']],203[['database' => 's12something']],204];205}206207private function getService(): DatabaseManagementService208{209return $this->app->make(DatabaseManagementService::class);210}211}212213214