Fix tests

This commit is contained in:
Dane Everitt 2017-10-23 20:12:15 -05:00
parent d50ea18598
commit 532025a348
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
10 changed files with 463 additions and 573 deletions

View file

@ -9,6 +9,7 @@
namespace Pterodactyl\Services\Databases; namespace Pterodactyl\Services\Databases;
use Pterodactyl\Models\Database;
use Illuminate\Database\DatabaseManager; use Illuminate\Database\DatabaseManager;
use Illuminate\Contracts\Encryption\Encrypter; use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection; use Pterodactyl\Extensions\DynamicDatabaseConnection;
@ -95,7 +96,7 @@ class DatabaseManagementService
$this->database->commit(); $this->database->commit();
} catch (\Exception $ex) { } catch (\Exception $ex) {
try { try {
if (isset($database)) { if (isset($database) && $database instanceof Database) {
$this->repository->dropDatabase($database->database); $this->repository->dropDatabase($database->database);
$this->repository->dropUser($database->username, $database->remote); $this->repository->dropUser($database->username, $database->remote);
$this->repository->flush(); $this->repository->flush();

View file

@ -39,6 +39,8 @@ $factory->define(Pterodactyl\Models\Server::class, function (Faker\Generator $fa
}); });
$factory->define(Pterodactyl\Models\User::class, function (Faker\Generator $faker) { $factory->define(Pterodactyl\Models\User::class, function (Faker\Generator $faker) {
static $password;
return [ return [
'id' => $faker->unique()->randomNumber(), 'id' => $faker->unique()->randomNumber(),
'external_id' => null, 'external_id' => null,
@ -47,7 +49,7 @@ $factory->define(Pterodactyl\Models\User::class, function (Faker\Generator $fake
'email' => $faker->safeEmail, 'email' => $faker->safeEmail,
'name_first' => $faker->firstName, 'name_first' => $faker->firstName,
'name_last' => $faker->lastName, 'name_last' => $faker->lastName,
'password' => bcrypt('password'), 'password' => $password ?: $password = bcrypt('password'),
'language' => 'en', 'language' => 'en',
'root_admin' => false, 'root_admin' => false,
'use_totp' => false, 'use_totp' => false,
@ -173,6 +175,21 @@ $factory->define(Pterodactyl\Models\DatabaseHost::class, function (Faker\Generat
]; ];
}); });
$factory->define(Pterodactyl\Models\Database::class, function (Faker\Generator $faker) {
static $password;
return [
'id' => $faker->unique()->randomNumber(),
'server_id' => $faker->randomNumber(),
'database_host_id' => $faker->randomNumber(),
'database' => str_random(10),
'username' => str_random(10),
'password' => $password ?: bcrypt('test123'),
'created_at' => \Carbon\Carbon::now()->toDateTimeString(),
'updated_at' => \Carbon\Carbon::now()->toDateTimeString(),
];
});
$factory->define(Pterodactyl\Models\Schedule::class, function (Faker\Generator $faker) { $factory->define(Pterodactyl\Models\Schedule::class, function (Faker\Generator $faker) {
return [ return [
'id' => $faker->unique()->randomNumber(), 'id' => $faker->unique()->randomNumber(),

View file

@ -14,6 +14,9 @@ use Tests\TestCase;
use Prologue\Alerts\AlertsMessageBag; use Prologue\Alerts\AlertsMessageBag;
use Tests\Assertions\ControllerAssertionsTrait; use Tests\Assertions\ControllerAssertionsTrait;
use Pterodactyl\Http\Controllers\Admin\DatabaseController; use Pterodactyl\Http\Controllers\Admin\DatabaseController;
use Pterodactyl\Services\Databases\Hosts\HostUpdateService;
use Pterodactyl\Services\Databases\Hosts\HostCreationService;
use Pterodactyl\Services\Databases\Hosts\HostDeletionService;
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
@ -22,29 +25,34 @@ class DatabaseControllerTest extends TestCase
use ControllerAssertionsTrait; use ControllerAssertionsTrait;
/** /**
* @var \Prologue\Alerts\AlertsMessageBag * @var \Prologue\Alerts\AlertsMessageBag|\Mockery\Mock
*/ */
protected $alert; private $alert;
/** /**
* @var \Pterodactyl\Http\Controllers\Admin\DatabaseController * @var \Pterodactyl\Services\Databases\Hosts\HostCreationService|\Mockery\Mock
*/ */
protected $controller; private $creationService;
/** /**
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface * @var \Pterodactyl\Services\Databases\Hosts\HostDeletionService|\Mockery\Mock
*/ */
protected $locationRepository; private $deletionService;
/** /**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface * @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface|\Mockery\Mock
*/ */
protected $repository; private $locationRepository;
/** /**
* @var \Pterodactyl\Services\Databases\HostsUpdateService * @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface|\Mockery\Mock
*/ */
protected $service; private $repository;
/**
* @var \Pterodactyl\Services\Databases\Hosts\HostUpdateService|\Mockery\Mock
*/
private $updateService;
/** /**
* Setup tests. * Setup tests.
@ -54,16 +62,11 @@ class DatabaseControllerTest extends TestCase
parent::setUp(); parent::setUp();
$this->alert = m::mock(AlertsMessageBag::class); $this->alert = m::mock(AlertsMessageBag::class);
$this->creationService = m::mock(HostCreationService::class);
$this->deletionService = m::mock(HostDeletionService::class);
$this->locationRepository = m::mock(LocationRepositoryInterface::class); $this->locationRepository = m::mock(LocationRepositoryInterface::class);
$this->repository = m::mock(DatabaseHostRepositoryInterface::class); $this->repository = m::mock(DatabaseHostRepositoryInterface::class);
$this->service = m::mock(HostUpdateService::class); $this->updateService = m::mock(HostUpdateService::class);
$this->controller = new DatabaseController(
$this->alert,
$this->repository,
$this->service,
$this->locationRepository
);
} }
/** /**
@ -74,7 +77,7 @@ class DatabaseControllerTest extends TestCase
$this->locationRepository->shouldReceive('getAllWithNodes')->withNoArgs()->once()->andReturn('getAllWithNodes'); $this->locationRepository->shouldReceive('getAllWithNodes')->withNoArgs()->once()->andReturn('getAllWithNodes');
$this->repository->shouldReceive('getWithViewDetails')->withNoArgs()->once()->andReturn('getWithViewDetails'); $this->repository->shouldReceive('getWithViewDetails')->withNoArgs()->once()->andReturn('getWithViewDetails');
$response = $this->controller->index(); $response = $this->getController()->index();
$this->assertIsViewResponse($response); $this->assertIsViewResponse($response);
$this->assertViewNameEquals('admin.databases.index', $response); $this->assertViewNameEquals('admin.databases.index', $response);
@ -92,7 +95,7 @@ class DatabaseControllerTest extends TestCase
$this->locationRepository->shouldReceive('getAllWithNodes')->withNoArgs()->once()->andReturn('getAllWithNodes'); $this->locationRepository->shouldReceive('getAllWithNodes')->withNoArgs()->once()->andReturn('getAllWithNodes');
$this->repository->shouldReceive('getWithServers')->with(1)->once()->andReturn('getWithServers'); $this->repository->shouldReceive('getWithServers')->with(1)->once()->andReturn('getWithServers');
$response = $this->controller->view(1); $response = $this->getController()->view(1);
$this->assertIsViewResponse($response); $this->assertIsViewResponse($response);
$this->assertViewNameEquals('admin.databases.view', $response); $this->assertViewNameEquals('admin.databases.view', $response);
@ -101,4 +104,21 @@ class DatabaseControllerTest extends TestCase
$this->assertViewKeyEquals('locations', 'getAllWithNodes', $response); $this->assertViewKeyEquals('locations', 'getAllWithNodes', $response);
$this->assertViewKeyEquals('host', 'getWithServers', $response); $this->assertViewKeyEquals('host', 'getWithServers', $response);
} }
/**
* Return an instance of the DatabaseController with mock dependencies.
*
* @return \Pterodactyl\Http\Controllers\Admin\DatabaseController
*/
private function getController(): DatabaseController
{
return new DatabaseController(
$this->alert,
$this->repository,
$this->creationService,
$this->deletionService,
$this->updateService,
$this->locationRepository
);
}
} }

View file

@ -96,7 +96,7 @@ class DatabaseRepositoryTest extends TestCase
public function testCreateDatabaseStatement() public function testCreateDatabaseStatement()
{ {
$query = sprintf('CREATE DATABASE IF NOT EXISTS `%s`', 'test_database'); $query = sprintf('CREATE DATABASE IF NOT EXISTS `%s`', 'test_database');
$this->repository->shouldReceive('runStatement')->with($query, 'test')->once()->andReturn(true); $this->repository->shouldReceive('runStatement')->with($query)->once()->andReturn(true);
$this->assertTrue($this->repository->createDatabase('test_database', 'test')); $this->assertTrue($this->repository->createDatabase('test_database', 'test'));
} }
@ -107,7 +107,7 @@ class DatabaseRepositoryTest extends TestCase
public function testCreateUserStatement() public function testCreateUserStatement()
{ {
$query = sprintf('CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'', 'test', '%', 'password'); $query = sprintf('CREATE USER `%s`@`%s` IDENTIFIED BY \'%s\'', 'test', '%', 'password');
$this->repository->shouldReceive('runStatement')->with($query, 'test')->once()->andReturn(true); $this->repository->shouldReceive('runStatement')->with($query)->once()->andReturn(true);
$this->assertTrue($this->repository->createUser('test', '%', 'password', 'test')); $this->assertTrue($this->repository->createUser('test', '%', 'password', 'test'));
} }
@ -118,7 +118,7 @@ class DatabaseRepositoryTest extends TestCase
public function testUserAssignmentToDatabaseStatement() public function testUserAssignmentToDatabaseStatement()
{ {
$query = sprintf('GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, EXECUTE ON `%s`.* TO `%s`@`%s`', 'test_database', 'test', '%'); $query = sprintf('GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, EXECUTE ON `%s`.* TO `%s`@`%s`', 'test_database', 'test', '%');
$this->repository->shouldReceive('runStatement')->with($query, 'test')->once()->andReturn(true); $this->repository->shouldReceive('runStatement')->with($query)->once()->andReturn(true);
$this->assertTrue($this->repository->assignUserToDatabase('test_database', 'test', '%', 'test')); $this->assertTrue($this->repository->assignUserToDatabase('test_database', 'test', '%', 'test'));
} }
@ -128,7 +128,7 @@ class DatabaseRepositoryTest extends TestCase
*/ */
public function testFlushStatement() public function testFlushStatement()
{ {
$this->repository->shouldReceive('runStatement')->with('FLUSH PRIVILEGES', 'test')->once()->andReturn(true); $this->repository->shouldReceive('runStatement')->with('FLUSH PRIVILEGES')->once()->andReturn(true);
$this->assertTrue($this->repository->flush('test')); $this->assertTrue($this->repository->flush('test'));
} }
@ -139,7 +139,7 @@ class DatabaseRepositoryTest extends TestCase
public function testDropDatabaseStatement() public function testDropDatabaseStatement()
{ {
$query = sprintf('DROP DATABASE IF EXISTS `%s`', 'test_database'); $query = sprintf('DROP DATABASE IF EXISTS `%s`', 'test_database');
$this->repository->shouldReceive('runStatement')->with($query, 'test')->once()->andReturn(true); $this->repository->shouldReceive('runStatement')->with($query)->once()->andReturn(true);
$this->assertTrue($this->repository->dropDatabase('test_database', 'test')); $this->assertTrue($this->repository->dropDatabase('test_database', 'test'));
} }
@ -150,7 +150,7 @@ class DatabaseRepositoryTest extends TestCase
public function testDropUserStatement() public function testDropUserStatement()
{ {
$query = sprintf('DROP USER IF EXISTS `%s`@`%s`', 'test', '%'); $query = sprintf('DROP USER IF EXISTS `%s`@`%s`', 'test', '%');
$this->repository->shouldReceive('runStatement')->with($query, 'test')->once()->andReturn(true); $this->repository->shouldReceive('runStatement')->with($query)->once()->andReturn(true);
$this->assertTrue($this->repository->dropUser('test', '%', 'test')); $this->assertTrue($this->repository->dropUser('test', '%', 'test'));
} }

View file

@ -1,201 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Tests\Unit\Services\Administrative;
use Mockery as m;
use Tests\TestCase;
use Illuminate\Database\DatabaseManager;
use Pterodactyl\Exceptions\DisplayException;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
class DatabaseHostServiceTest extends TestCase
{
/**
* @var \Illuminate\Database\DatabaseManager
*/
protected $database;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
*/
protected $databaseRepository;
/**
* @var \Pterodactyl\Extensions\DynamicDatabaseConnection
*/
protected $dynamic;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
protected $encrypter;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Services\Databases\HostsUpdateService
*/
protected $service;
/**
* Setup tests.
*/
public function setUp()
{
parent::setUp();
$this->database = m::mock(DatabaseManager::class);
$this->databaseRepository = m::mock(DatabaseRepositoryInterface::class);
$this->dynamic = m::mock(DynamicDatabaseConnection::class);
$this->encrypter = m::mock(Encrypter::class);
$this->repository = m::mock(DatabaseHostRepositoryInterface::class);
$this->service = new HostUpdateService(
$this->database,
$this->databaseRepository,
$this->repository,
$this->dynamic,
$this->encrypter
);
}
/**
* Test that creating a host returns the correct data.
*/
public function testHostIsCreated()
{
$data = [
'password' => 'raw-password',
'name' => 'HostName',
'host' => '127.0.0.1',
'port' => 3306,
'username' => 'someusername',
'node_id' => null,
];
$finalData = (object) array_replace($data, ['password' => 'enc-password']);
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->encrypter->shouldReceive('encrypt')->with('raw-password')->once()->andReturn('enc-password');
$this->repository->shouldReceive('create')->with([
'password' => 'enc-password',
'name' => 'HostName',
'host' => '127.0.0.1',
'port' => 3306,
'username' => 'someusername',
'max_databases' => null,
'node_id' => null,
])->once()->andReturn($finalData);
$this->dynamic->shouldReceive('set')->with('dynamic', $finalData)->once()->andReturnNull();
$this->database->shouldReceive('connection')->with('dynamic')->once()->andReturnSelf()
->shouldReceive('select')->with('SELECT 1 FROM dual')->once()->andReturnNull();
$this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->service->create($data);
$this->assertNotNull($response);
$this->assertTrue(is_object($response), 'Assert that response is an object.');
$this->assertEquals('enc-password', $response->password);
$this->assertEquals('HostName', $response->name);
$this->assertEquals('127.0.0.1', $response->host);
$this->assertEquals(3306, $response->port);
$this->assertEquals('someusername', $response->username);
$this->assertNull($response->node_id);
}
/**
* Test that passing a password will store an encrypted version in the DB.
*/
public function testHostIsUpdatedWithPasswordProvided()
{
$finalData = (object) ['password' => 'enc-pass', 'host' => '123.456.78.9'];
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->encrypter->shouldReceive('encrypt')->with('raw-pass')->once()->andReturn('enc-pass');
$this->repository->shouldReceive('update')->with(1, [
'password' => 'enc-pass',
'host' => '123.456.78.9',
])->once()->andReturn($finalData);
$this->dynamic->shouldReceive('set')->with('dynamic', $finalData)->once()->andReturnNull();
$this->database->shouldReceive('connection')->with('dynamic')->once()->andReturnSelf()
->shouldReceive('select')->with('SELECT 1 FROM dual')->once()->andReturnNull();
$this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->service->update(1, ['password' => 'raw-pass', 'host' => '123.456.78.9']);
$this->assertNotNull($response);
$this->assertEquals('enc-pass', $response->password);
$this->assertEquals('123.456.78.9', $response->host);
}
/**
* Test that passing no or empty password will skip storing it.
*/
public function testHostIsUpdatedWithoutPassword()
{
$finalData = (object) ['host' => '123.456.78.9'];
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->encrypter->shouldNotReceive('encrypt');
$this->repository->shouldReceive('update')->with(1, ['host' => '123.456.78.9'])->once()->andReturn($finalData);
$this->dynamic->shouldReceive('set')->with('dynamic', $finalData)->once()->andReturnNull();
$this->database->shouldReceive('connection')->with('dynamic')->once()->andReturnSelf()
->shouldReceive('select')->with('SELECT 1 FROM dual')->once()->andReturnNull();
$this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->service->update(1, ['password' => '', 'host' => '123.456.78.9']);
$this->assertNotNull($response);
$this->assertEquals('123.456.78.9', $response->host);
}
/**
* Test that a database host can be deleted.
*/
public function testHostIsDeleted()
{
$this->databaseRepository->shouldReceive('findCountWhere')->with([['database_host_id', '=', 1]])->once()->andReturn(0);
$this->repository->shouldReceive('delete')->with(1)->once()->andReturn(true);
$response = $this->service->delete(1);
$this->assertTrue($response, 'Assert that response is true.');
}
/**
* Test exception is thrown when there are databases attached to a host.
*/
public function testExceptionIsThrownIfHostHasDatabases()
{
$this->databaseRepository->shouldReceive('findCountWhere')->with([['database_host_id', '=', 1]])->once()->andReturn(2);
try {
$this->service->delete(1);
} catch (DisplayException $exception) {
$this->assertEquals(trans('exceptions.databases.delete_has_databases'), $exception->getMessage());
}
}
}

View file

@ -1,344 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* This software is licensed under the terms of the MIT license.
* https://opensource.org/licenses/MIT
*/
namespace Tests\Unit\Services\Database;
use Exception;
use Mockery as m;
use Tests\TestCase;
use phpmock\phpunit\PHPMock;
use Illuminate\Database\DatabaseManager;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection;
use Pterodactyl\Services\Databases\DatabaseManagementService;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
class DatabaseManagementServiceTest extends TestCase
{
use PHPMock;
const TEST_DATA = [
'server_id' => 1,
'database' => 'd1_dbname',
'remote' => '%',
'username' => 'u1_str_random',
'password' => 'enc_password',
'database_host_id' => 3,
];
/**
* @var \Illuminate\Database\DatabaseManager
*/
protected $database;
/**
* @var \Pterodactyl\Extensions\DynamicDatabaseConnection
*/
protected $dynamic;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
protected $encrypter;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
*/
protected $repository;
/**
* @var \Pterodactyl\Services\Databases\DatabaseManagementService
*/
protected $service;
/**
* Setup tests.
*/
public function setUp()
{
parent::setUp();
$this->database = m::mock(DatabaseManager::class);
$this->dynamic = m::mock(DynamicDatabaseConnection::class);
$this->encrypter = m::mock(Encrypter::class);
$this->repository = m::mock(DatabaseRepositoryInterface::class);
$this->getFunctionMock('\\Pterodactyl\\Services\\Database', 'str_random')
->expects($this->any())->willReturn('str_random');
$this->service = new DatabaseManagementService(
$this->database,
$this->dynamic,
$this->repository,
$this->encrypter
);
}
/**
* Test that a new database can be created that is linked to a specific host.
*/
public function testCreateANewDatabaseThatIsLinkedToAHost()
{
$this->encrypter->shouldReceive('encrypt')->with('str_random')->once()->andReturn('enc_password');
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('createIfNotExists')
->with(self::TEST_DATA)
->once()
->andReturn((object) self::TEST_DATA);
$this->dynamic->shouldReceive('set')
->with('dynamic', self::TEST_DATA['database_host_id'])
->once()
->andReturnNull();
$this->repository->shouldReceive('createDatabase')->with(
self::TEST_DATA['database'],
'dynamic'
)->once()->andReturnNull();
$this->encrypter->shouldReceive('decrypt')->with('enc_password')->once()->andReturn('str_random');
$this->repository->shouldReceive('createUser')->with(
self::TEST_DATA['username'],
self::TEST_DATA['remote'],
'str_random',
'dynamic'
)->once()->andReturnNull();
$this->repository->shouldReceive('assignUserToDatabase')->with(
self::TEST_DATA['database'],
self::TEST_DATA['username'],
self::TEST_DATA['remote'],
'dynamic'
)->once()->andReturnNull();
$this->repository->shouldReceive('flush')->with('dynamic')->once()->andReturnNull();
$this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->service->create(1, [
'database' => 'dbname',
'remote' => '%',
'database_host_id' => 3,
]);
$this->assertNotEmpty($response);
$this->assertTrue(is_object($response), 'Assert that response is an object.');
$this->assertEquals(self::TEST_DATA['database'], $response->database);
$this->assertEquals(self::TEST_DATA['remote'], $response->remote);
$this->assertEquals(self::TEST_DATA['username'], $response->username);
$this->assertEquals(self::TEST_DATA['password'], $response->password);
$this->assertEquals(self::TEST_DATA['database_host_id'], $response->database_host_id);
}
/**
* Test that an exception before the database is created and returned does not attempt any actions.
*
* @expectedException \Exception
*/
public function testExceptionBeforeDatabaseIsCreatedShouldNotAttemptAnyRollBackOperations()
{
$this->encrypter->shouldReceive('encrypt')->with('str_random')->once()->andReturn('enc_password');
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('createIfNotExists')
->with(self::TEST_DATA)
->once()
->andThrow(new Exception('Test Message'));
$this->repository->shouldNotReceive('dropDatabase');
$this->database->shouldReceive('rollBack')->withNoArgs()->once()->andReturnNull();
$this->service->create(1, [
'database' => 'dbname',
'remote' => '%',
'database_host_id' => 3,
]);
}
/**
* Test that an exception after database creation attempts to clean up previous operations.
*
* @expectedException \Exception
*/
public function testExceptionAfterDatabaseCreationShouldAttemptRollBackOperations()
{
$this->encrypter->shouldReceive('encrypt')->with('str_random')->once()->andReturn('enc_password');
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('createIfNotExists')
->with(self::TEST_DATA)
->once()
->andReturn((object) self::TEST_DATA);
$this->dynamic->shouldReceive('set')
->with('dynamic', self::TEST_DATA['database_host_id'])
->once()
->andReturnNull();
$this->repository->shouldReceive('createDatabase')->with(
self::TEST_DATA['database'],
'dynamic'
)->once()->andThrow(new Exception('Test Message'));
$this->repository->shouldReceive('dropDatabase')
->with(self::TEST_DATA['database'], 'dynamic')
->once()
->andReturnNull();
$this->repository->shouldReceive('dropUser')->with(
self::TEST_DATA['username'],
self::TEST_DATA['remote'],
'dynamic'
)->once()->andReturnNull();
$this->repository->shouldReceive('flush')->with('dynamic')->once()->andReturnNull();
$this->database->shouldReceive('rollBack')->withNoArgs()->once()->andReturnNull();
$this->service->create(1, [
'database' => 'dbname',
'remote' => '%',
'database_host_id' => 3,
]);
}
/**
* Test that an exception thrown during a rollback operation is silently handled and not returned.
*/
public function testExceptionThrownDuringRollBackProcessShouldNotBeThrownToCallingFunction()
{
$this->encrypter->shouldReceive('encrypt')->with('str_random')->once()->andReturn('enc_password');
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('createIfNotExists')
->with(self::TEST_DATA)
->once()
->andReturn((object) self::TEST_DATA);
$this->dynamic->shouldReceive('set')
->with('dynamic', self::TEST_DATA['database_host_id'])
->once()
->andReturnNull();
$this->repository->shouldReceive('createDatabase')->with(
self::TEST_DATA['database'],
'dynamic'
)->once()->andThrow(new Exception('Test One'));
$this->repository->shouldReceive('dropDatabase')->with(self::TEST_DATA['database'], 'dynamic')
->once()->andThrow(new Exception('Test Two'));
$this->database->shouldReceive('rollBack')->withNoArgs()->once()->andReturnNull();
try {
$this->service->create(1, [
'database' => 'dbname',
'remote' => '%',
'database_host_id' => 3,
]);
} catch (Exception $ex) {
$this->assertInstanceOf(Exception::class, $ex);
$this->assertEquals('Test One', $ex->getMessage());
}
}
/**
* Test that a password can be changed for a given database.
*/
public function testDatabasePasswordShouldBeChanged()
{
$this->repository->shouldReceive('find')->with(1)->once()->andReturn((object) self::TEST_DATA);
$this->dynamic->shouldReceive('set')
->with('dynamic', self::TEST_DATA['database_host_id'])
->once()
->andReturnNull();
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->encrypter->shouldReceive('encrypt')->with('new_password')->once()->andReturn('new_enc_password');
$this->repository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf()
->shouldReceive('update')->with(1, [
'password' => 'new_enc_password',
])->andReturn(true);
$this->repository->shouldReceive('dropUser')->with(
self::TEST_DATA['username'],
self::TEST_DATA['remote'],
'dynamic'
)->once()->andReturnNull();
$this->repository->shouldReceive('createUser')->with(
self::TEST_DATA['username'],
self::TEST_DATA['remote'],
'new_password',
'dynamic'
)->once()->andReturnNull();
$this->repository->shouldReceive('assignUserToDatabase')->with(
self::TEST_DATA['database'],
self::TEST_DATA['username'],
self::TEST_DATA['remote'],
'dynamic'
)->once()->andReturnNull();
$this->repository->shouldReceive('flush')->with('dynamic')->once()->andReturnNull();
$this->database->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->service->changePassword(1, 'new_password');
$this->assertTrue($response);
}
/**
* Test that an exception thrown while changing a password will attempt a rollback.
*
* @expectedException \Exception
*/
public function testExceptionThrownWhileChangingDatabasePasswordShouldRollBack()
{
$this->repository->shouldReceive('find')->with(1)->once()->andReturn((object) self::TEST_DATA);
$this->dynamic->shouldReceive('set')
->with('dynamic', self::TEST_DATA['database_host_id'])
->once()
->andReturnNull();
$this->database->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->encrypter->shouldReceive('encrypt')->with('new_password')->once()->andReturn('new_enc_password');
$this->repository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf()
->shouldReceive('update')->with(1, [
'password' => 'new_enc_password',
])->andReturn(true);
$this->repository->shouldReceive('dropUser')->with(
self::TEST_DATA['username'],
self::TEST_DATA['remote'],
'dynamic'
)->once()->andThrow(new Exception());
$this->database->shouldReceive('rollBack')->withNoArgs()->once()->andReturnNull();
$this->service->changePassword(1, 'new_password');
}
/**
* Test that a database can be deleted.
*/
public function testDatabaseShouldBeDeleted()
{
$this->repository->shouldReceive('find')->with(1)->once()->andReturn((object) self::TEST_DATA);
$this->dynamic->shouldReceive('set')
->with('dynamic', self::TEST_DATA['database_host_id'])
->once()
->andReturnNull();
$this->repository->shouldReceive('dropDatabase')
->with(self::TEST_DATA['database'], 'dynamic')
->once()
->andReturnNull();
$this->repository->shouldReceive('dropUser')->with(
self::TEST_DATA['username'],
self::TEST_DATA['remote'],
'dynamic'
)->once()->andReturnNull();
$this->repository->shouldReceive('flush')->with('dynamic')->once()->andReturnNull();
$this->repository->shouldReceive('delete')->with(1)->once()->andReturn(1);
$response = $this->service->delete(1);
$this->assertEquals(1, $response);
}
}

View file

@ -0,0 +1,99 @@
<?php
namespace Tests\Unit\Services\Databases;
use Mockery as m;
use Tests\TestCase;
use Pterodactyl\Models\Database;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection;
use Pterodactyl\Services\Databases\DatabasePasswordService;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
class DatabasePasswordServiceTest extends TestCase
{
/**
* @var \Illuminate\Database\ConnectionInterface|\Mockery\Mock
*/
private $connection;
/**
* @var \Pterodactyl\Extensions\DynamicDatabaseConnection|\Mockery\Mock
*/
private $dynamic;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter|\Mockery\Mock
*/
private $encrypter;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface|\Mockery\Mock
*/
private $repository;
/**
* Setup tests.
*/
public function setUp()
{
parent::setUp();
$this->connection = m::mock(ConnectionInterface::class);
$this->dynamic = m::mock(DynamicDatabaseConnection::class);
$this->encrypter = m::mock(Encrypter::class);
$this->repository = m::mock(DatabaseRepositoryInterface::class);
}
/**
* Test that a password can be updated.
*
* @dataProvider useModelDataProvider
*/
public function testPasswordIsChanged($useModel)
{
$model = factory(Database::class)->make();
if (! $useModel) {
$this->repository->shouldReceive('find')->with(1234)->once()->andReturn($model);
}
$this->dynamic->shouldReceive('set')->with('dynamic', $model->database_host_id)->once()->andReturnNull();
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->encrypter->shouldReceive('encrypt')->with('test123')->once()->andReturn('enc123');
$this->repository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf();
$this->repository->shouldReceive('update')->with($model->id, ['password' => 'enc123'])->once()->andReturn(true);
$this->repository->shouldReceive('dropUser')->with($model->username, $model->remote)->once()->andReturnNull();
$this->repository->shouldReceive('createUser')->with($model->username, $model->remote, 'test123')->once()->andReturnNull();
$this->repository->shouldReceive('assignUserToDatabase')->with($model->database, $model->username, $model->remote)->once()->andReturnNull();
$this->repository->shouldReceive('flush')->withNoArgs()->once()->andReturnNull();
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->getService()->handle($useModel ? $model : 1234, 'test123');
$this->assertNotEmpty($response);
$this->assertTrue($response);
}
/**
* Data provider to determine if a model should be passed or an int.
*
* @return array
*/
public function useModelDataProvider(): array
{
return [[false], [true]];
}
/**
* Return an instance of the service with mocked dependencies.
*
* @return \Pterodactyl\Services\Databases\DatabasePasswordService
*/
private function getService(): DatabasePasswordService
{
return new DatabasePasswordService($this->connection, $this->repository, $this->dynamic, $this->encrypter);
}
}

View file

@ -0,0 +1,101 @@
<?php
namespace Tests\Unit\Services\Databases\Hosts;
use Mockery as m;
use Tests\TestCase;
use Pterodactyl\Models\DatabaseHost;
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection;
use Pterodactyl\Services\Databases\Hosts\HostCreationService;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
class HostCreationServiceTest extends TestCase
{
/**
* @var \Illuminate\Database\ConnectionInterface|\Mockery\Mock
*/
private $connection;
/**
* @var \Illuminate\Database\DatabaseManager|\Mockery\Mock
*/
private $databaseManager;
/**
* @var \Pterodactyl\Extensions\DynamicDatabaseConnection|\Mockery\Mock
*/
private $dynamic;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter|\Mockery\Mock
*/
private $encrypter;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface|\Mockery\Mock
*/
private $repository;
/**
* Setup tests.
*/
public function setUp()
{
parent::setUp();
$this->connection = m::mock(ConnectionInterface::class);
$this->databaseManager = m::mock(DatabaseManager::class);
$this->dynamic = m::mock(DynamicDatabaseConnection::class);
$this->encrypter = m::mock(Encrypter::class);
$this->repository = m::mock(DatabaseHostRepositoryInterface::class);
}
/**
* Test that a database host can be created.
*/
public function testDatabaseHostIsCreated()
{
$model = factory(DatabaseHost::class)->make();
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->encrypter->shouldReceive('encrypt')->with('test123')->once()->andReturn('enc123');
$this->repository->shouldReceive('create')->with(m::subset([
'password' => 'enc123',
'username' => $model->username,
'node_id' => $model->node_id,
]))->once()->andReturn($model);
$this->dynamic->shouldReceive('set')->with('dynamic', $model)->once()->andReturnNull();
$this->databaseManager->shouldReceive('connection')->with('dynamic')->once()->andReturnSelf();
$this->databaseManager->shouldReceive('select')->with('SELECT 1 FROM dual')->once()->andReturnNull();
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->getService()->handle([
'password' => 'test123',
'username' => $model->username,
'node_id' => $model->node_id,
]);
$this->assertNotEmpty($response);
$this->assertSame($model, $response);
}
/**
* Return an instance of the service with mocked dependencies.
*
* @return \Pterodactyl\Services\Databases\Hosts\HostCreationService
*/
private function getService(): HostCreationService
{
return new HostCreationService(
$this->connection,
$this->databaseManager,
$this->repository,
$this->dynamic,
$this->encrypter
);
}
}

View file

@ -0,0 +1,85 @@
<?php
namespace Tests\Unit\Services\Databases\Hosts;
use Mockery as m;
use Tests\TestCase;
use Pterodactyl\Exceptions\PterodactylException;
use Pterodactyl\Exceptions\Service\HasActiveServersException;
use Pterodactyl\Services\Databases\Hosts\HostDeletionService;
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
class HostDeletionServiceTest extends TestCase
{
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface|\Mockery\Mock
*/
private $databaseRepository;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface|\Mockery\Mock
*/
private $repository;
/**
* Setup tests.
*/
public function setUp()
{
parent::setUp();
$this->databaseRepository = m::mock(DatabaseRepositoryInterface::class);
$this->repository = m::mock(DatabaseHostRepositoryInterface::class);
}
/**
* Test that a host can be deleted.
*/
public function testHostIsDeleted()
{
$this->databaseRepository->shouldReceive('findCountWhere')->with([['database_host_id', '=', 1234]])->once()->andReturn(0);
$this->repository->shouldReceive('delete')->with(1234)->once()->andReturn(1);
$response = $this->getService()->handle(1234);
$this->assertNotEmpty($response);
$this->assertSame(1, $response);
}
/**
* Test that an exception is thrown if a host with databases is deleted.
*
* @dataProvider databaseCountDataProvider
*/
public function testExceptionIsThrownIfDeletingHostWithDatabases($count)
{
$this->databaseRepository->shouldReceive('findCountWhere')->with([['database_host_id', '=', 1234]])->once()->andReturn($count);
try {
$this->getService()->handle(1234);
} catch (PterodactylException $exception) {
$this->assertInstanceOf(HasActiveServersException::class, $exception);
$this->assertEquals(trans('exceptions.databases.delete_has_databases'), $exception->getMessage());
}
}
/**
* Data provider to ensure exceptions are thrown for any value > 0.
*
* @return array
*/
public function databaseCountDataProvider(): array
{
return [[1], [2], [10]];
}
/**
* Return an instance of the service with mocked dependencies.
*
* @return \Pterodactyl\Services\Databases\Hosts\HostDeletionService
*/
private function getService(): HostDeletionService
{
return new HostDeletionService($this->databaseRepository, $this->repository);
}
}

View file

@ -0,0 +1,112 @@
<?php
namespace Tests\Unit\Services\Databases\Hosts;
use Mockery as m;
use Tests\TestCase;
use Pterodactyl\Models\DatabaseHost;
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection;
use Pterodactyl\Services\Databases\Hosts\HostUpdateService;
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
class HostUpdateServiceTest extends TestCase
{
/**
* @var \Illuminate\Database\ConnectionInterface|\Mockery\Mock
*/
private $connection;
/**
* @var \Illuminate\Database\DatabaseManager|\Mockery\Mock
*/
private $databaseManager;
/**
* @var \Pterodactyl\Extensions\DynamicDatabaseConnection|\Mockery\Mock
*/
private $dynamic;
/**
* @var \Illuminate\Contracts\Encryption\Encrypter|\Mockery\Mock
*/
private $encrypter;
/**
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface|\Mockery\Mock
*/
private $repository;
/**
* Setup tests.
*/
public function setUp()
{
parent::setUp();
$this->connection = m::mock(ConnectionInterface::class);
$this->databaseManager = m::mock(DatabaseManager::class);
$this->dynamic = m::mock(DynamicDatabaseConnection::class);
$this->encrypter = m::mock(Encrypter::class);
$this->repository = m::mock(DatabaseHostRepositoryInterface::class);
}
/**
* Test that a password is encrypted before storage if provided.
*/
public function testPasswordIsEncryptedWhenProvided()
{
$model = factory(DatabaseHost::class)->make();
$this->encrypter->shouldReceive('encrypt')->with('test123')->once()->andReturn('enc123');
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('update')->with(1234, ['password' => 'enc123'])->once()->andReturn($model);
$this->dynamic->shouldReceive('set')->with('dynamic', $model)->once()->andReturnNull();
$this->databaseManager->shouldReceive('connection')->with('dynamic')->once()->andReturnSelf();
$this->databaseManager->shouldReceive('select')->with('SELECT 1 FROM dual')->once()->andReturnNull();
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->getService()->handle(1234, ['password' => 'test123']);
$this->assertNotEmpty($response);
$this->assertSame($model, $response);
}
/**
* Test that updates still occur when no password is provided.
*/
public function testUpdateOccursWhenNoPasswordIsProvided()
{
$model = factory(DatabaseHost::class)->make();
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
$this->repository->shouldReceive('update')->with(1234, ['username' => 'test'])->once()->andReturn($model);
$this->dynamic->shouldReceive('set')->with('dynamic', $model)->once()->andReturnNull();
$this->databaseManager->shouldReceive('connection')->with('dynamic')->once()->andReturnSelf();
$this->databaseManager->shouldReceive('select')->with('SELECT 1 FROM dual')->once()->andReturnNull();
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
$response = $this->getService()->handle(1234, ['password' => '', 'username' => 'test']);
$this->assertNotEmpty($response);
$this->assertSame($model, $response);
}
/**
* Return an instance of the service with mocked dependencies.
*
* @return \Pterodactyl\Services\Databases\Hosts\HostUpdateService
*/
private function getService(): HostUpdateService
{
return new HostUpdateService(
$this->connection,
$this->databaseManager,
$this->repository,
$this->dynamic,
$this->encrypter
);
}
}