Don't waste time on a service better suited to an integration test
This commit is contained in:
parent
2560163655
commit
c59a2c436b
3 changed files with 63 additions and 340 deletions
|
@ -4,7 +4,9 @@ namespace Pterodactyl\Services\Servers;
|
|||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Illuminate\Support\Arr;
|
||||
use Pterodactyl\Models\Egg;
|
||||
use Pterodactyl\Models\User;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Support\Collection;
|
||||
use Pterodactyl\Models\Allocation;
|
||||
|
@ -13,7 +15,6 @@ use Pterodactyl\Models\Objects\DeploymentObject;
|
|||
use Pterodactyl\Repositories\Eloquent\EggRepository;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
||||
use Pterodactyl\Repositories\Eloquent\AllocationRepository;
|
||||
use Pterodactyl\Services\Deployment\FindViableNodesService;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerVariableRepository;
|
||||
use Pterodactyl\Services\Deployment\AllocationSelectionService;
|
||||
|
@ -21,11 +22,6 @@ use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
|||
|
||||
class ServerCreationService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\AllocationRepository
|
||||
*/
|
||||
private $allocationRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Deployment\AllocationSelectionService
|
||||
*/
|
||||
|
@ -79,7 +75,6 @@ class ServerCreationService
|
|||
/**
|
||||
* CreationService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Repositories\Eloquent\AllocationRepository $allocationRepository
|
||||
* @param \Pterodactyl\Services\Deployment\AllocationSelectionService $allocationSelectionService
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository
|
||||
|
@ -92,7 +87,6 @@ class ServerCreationService
|
|||
* @param \Pterodactyl\Services\Servers\VariableValidatorService $validatorService
|
||||
*/
|
||||
public function __construct(
|
||||
AllocationRepository $allocationRepository,
|
||||
AllocationSelectionService $allocationSelectionService,
|
||||
ConnectionInterface $connection,
|
||||
DaemonServerRepository $daemonServerRepository,
|
||||
|
@ -105,7 +99,6 @@ class ServerCreationService
|
|||
VariableValidatorService $validatorService
|
||||
) {
|
||||
$this->allocationSelectionService = $allocationSelectionService;
|
||||
$this->allocationRepository = $allocationRepository;
|
||||
$this->configurationStructureService = $configurationStructureService;
|
||||
$this->connection = $connection;
|
||||
$this->findViableNodesService = $findViableNodesService;
|
||||
|
@ -149,14 +142,16 @@ class ServerCreationService
|
|||
|
||||
// Auto-configure the node based on the selected allocation
|
||||
// if no node was defined.
|
||||
if (is_null(Arr::get($data, 'node_id'))) {
|
||||
$data['node_id'] = $this->getNodeFromAllocation($data['allocation_id']);
|
||||
if (empty($data['node_id'])) {
|
||||
Assert::false(empty($data['allocation_id']), 'Expected a non-empty allocation_id in server creation data.');
|
||||
|
||||
$data['node_id'] = Allocation::query()->findOrFail($data['allocation_id'])->node_id;
|
||||
}
|
||||
|
||||
if (is_null(Arr::get($data, 'nest_id'))) {
|
||||
/** @var \Pterodactyl\Models\Egg $egg */
|
||||
$egg = $this->eggRepository->setColumns(['id', 'nest_id'])->find(Arr::get($data, 'egg_id'));
|
||||
$data['nest_id'] = $egg->nest_id;
|
||||
if (empty($data['nest_id'])) {
|
||||
Assert::false(empty($data['egg_id']), 'Expected a non-empty egg_id in server creation data.');
|
||||
|
||||
$data['nest_id'] = Egg::query()->findOrFail($data['egg_id'])->nest_id;
|
||||
}
|
||||
|
||||
$eggVariableData = $this->validatorService
|
||||
|
@ -269,7 +264,7 @@ class ServerCreationService
|
|||
$records = array_merge($records, $data['allocation_additional']);
|
||||
}
|
||||
|
||||
$this->allocationRepository->updateWhereIn('id', $records, [
|
||||
Allocation::query()->whereIn('id', $records)->update([
|
||||
'server_id' => $server->id,
|
||||
]);
|
||||
}
|
||||
|
@ -295,22 +290,6 @@ class ServerCreationService
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node that an allocation belongs to.
|
||||
*
|
||||
* @param int $id
|
||||
* @return int
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
private function getNodeFromAllocation(int $id): int
|
||||
{
|
||||
/** @var \Pterodactyl\Models\Allocation $allocation */
|
||||
$allocation = $this->allocationRepository->setColumns(['id', 'node_id'])->find($id);
|
||||
|
||||
return $allocation->node_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a unique UUID and UUID-Short combo for a server.
|
||||
*
|
||||
|
|
52
tests/Traits/MocksPdoConnection.php
Normal file
52
tests/Traits/MocksPdoConnection.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Traits;
|
||||
|
||||
use PDO;
|
||||
use Mockery;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\MySqlConnection;
|
||||
use Illuminate\Database\ConnectionResolver;
|
||||
|
||||
trait MocksPdoConnection
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Database\ConnectionResolverInterface|null
|
||||
*/
|
||||
private static $initialResolver;
|
||||
|
||||
/**
|
||||
* Generates a mock PDO connection and injects it into the models so that any actual
|
||||
* DB call can be properly intercepted.
|
||||
*
|
||||
* @return \Mockery\MockInterface
|
||||
*/
|
||||
protected function mockPdoConnection()
|
||||
{
|
||||
self::$initialResolver = Model::getConnectionResolver();
|
||||
|
||||
Model::unsetConnectionResolver();
|
||||
|
||||
$connection = new MySqlConnection($mock = Mockery::mock(PDO::class), 'testing_mock');
|
||||
$resolver = new ConnectionResolver(['mocked' => $connection]);
|
||||
$resolver->setDefaultConnection('mocked');
|
||||
|
||||
Model::setConnectionResolver($resolver);
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the mock state.
|
||||
*/
|
||||
protected function tearDownPdoMock()
|
||||
{
|
||||
if (! self::$initialResolver) {
|
||||
return;
|
||||
}
|
||||
|
||||
Model::setConnectionResolver(self::$initialResolver);
|
||||
|
||||
self::$initialResolver = null;
|
||||
}
|
||||
}
|
|
@ -1,308 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Services\Servers;
|
||||
|
||||
use Mockery as m;
|
||||
use Tests\TestCase;
|
||||
use Pterodactyl\Models\Egg;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use Pterodactyl\Models\User;
|
||||
use Tests\Traits\MocksUuids;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\Allocation;
|
||||
use Tests\Traits\MocksRequestException;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Models\Objects\DeploymentObject;
|
||||
use Pterodactyl\Repositories\Eloquent\EggRepository;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Services\Servers\ServerCreationService;
|
||||
use Pterodactyl\Services\Servers\ServerDeletionService;
|
||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
||||
use Pterodactyl\Services\Servers\VariableValidatorService;
|
||||
use Pterodactyl\Repositories\Eloquent\AllocationRepository;
|
||||
use Pterodactyl\Services\Deployment\FindViableNodesService;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerVariableRepository;
|
||||
use Pterodactyl\Services\Deployment\AllocationSelectionService;
|
||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||
use Pterodactyl\Services\Servers\ServerConfigurationStructureService;
|
||||
|
||||
/**
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
class ServerCreationServiceTest extends TestCase
|
||||
{
|
||||
use MocksRequestException, MocksUuids;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $allocationRepository;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $allocationSelectionService;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $configurationStructureService;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $daemonServerRepository;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $eggRepository;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $findViableNodesService;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $serverVariableRepository;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $validatorService;
|
||||
|
||||
/**
|
||||
* @var \Mockery\MockInterface
|
||||
*/
|
||||
private $serverDeletionService;
|
||||
|
||||
/**
|
||||
* Setup tests.
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->allocationRepository = m::mock(AllocationRepository::class);
|
||||
$this->allocationSelectionService = m::mock(AllocationSelectionService::class);
|
||||
$this->configurationStructureService = m::mock(ServerConfigurationStructureService::class);
|
||||
$this->connection = m::mock(ConnectionInterface::class);
|
||||
$this->findViableNodesService = m::mock(FindViableNodesService::class);
|
||||
$this->validatorService = m::mock(VariableValidatorService::class);
|
||||
$this->eggRepository = m::mock(EggRepository::class);
|
||||
$this->repository = m::mock(ServerRepository::class);
|
||||
$this->serverVariableRepository = m::mock(ServerVariableRepository::class);
|
||||
$this->daemonServerRepository = m::mock(DaemonServerRepository::class);
|
||||
$this->serverDeletionService = m::mock(ServerDeletionService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test core functionality of the creation process.
|
||||
*/
|
||||
public function testCreateShouldHitAllOfTheNecessaryServicesAndStoreTheServer()
|
||||
{
|
||||
$model = factory(Server::class)->make([
|
||||
'uuid' => $this->getKnownUuid(),
|
||||
]);
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
$this->repository->shouldReceive('isUniqueUuidCombo')
|
||||
->once()
|
||||
->with($this->getKnownUuid(), substr($this->getKnownUuid(), 0, 8))
|
||||
->andReturn(true);
|
||||
|
||||
$this->repository->shouldReceive('create')->with(m::subset([
|
||||
'uuid' => $this->getKnownUuid(),
|
||||
'uuidShort' => substr($this->getKnownUuid(), 0, 8),
|
||||
'node_id' => $model->node_id,
|
||||
'allocation_id' => $model->allocation_id,
|
||||
'owner_id' => $model->owner_id,
|
||||
'nest_id' => $model->nest_id,
|
||||
'egg_id' => $model->egg_id,
|
||||
]))->once()->andReturn($model);
|
||||
|
||||
$this->allocationRepository->shouldReceive('assignAllocationsToServer')->with($model->id, [$model->allocation_id])->once()->andReturn(1);
|
||||
|
||||
$this->validatorService->shouldReceive('setUserLevel')->with(User::USER_LEVEL_ADMIN)->once()->andReturnSelf();
|
||||
$this->validatorService->shouldReceive('handle')->with($model->egg_id, [])->once()->andReturn(
|
||||
collect([(object) ['id' => 123, 'value' => 'var1-value']])
|
||||
);
|
||||
|
||||
$this->serverVariableRepository->shouldReceive('insert')->with([
|
||||
[
|
||||
'server_id' => $model->id,
|
||||
'variable_id' => 123,
|
||||
'variable_value' => 'var1-value',
|
||||
],
|
||||
])->once()->andReturn(true);
|
||||
$this->configurationStructureService->shouldReceive('handle')->with($model)->once()->andReturn(['test' => 'struct']);
|
||||
|
||||
$this->daemonServerRepository->shouldReceive('setServer')->with($model)->once()->andReturnSelf();
|
||||
$this->daemonServerRepository->shouldReceive('create')->with(['test' => 'struct'])->once();
|
||||
$this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull();
|
||||
|
||||
$response = $this->getService()->handle($model->toArray());
|
||||
|
||||
$this->assertSame($model, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that optional parameters get auto-filled correctly on the model.
|
||||
*/
|
||||
public function testDataIsAutoFilled()
|
||||
{
|
||||
$model = factory(Server::class)->make(['uuid' => $this->getKnownUuid()]);
|
||||
$allocationModel = factory(Allocation::class)->make(['node_id' => $model->node_id]);
|
||||
$eggModel = factory(Egg::class)->make(['nest_id' => $model->nest_id]);
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs();
|
||||
$this->allocationRepository->shouldReceive('setColumns->find')->once()->with($model->allocation_id)->andReturn($allocationModel);
|
||||
$this->eggRepository->shouldReceive('setColumns->find')->once()->with($model->egg_id)->andReturn($eggModel);
|
||||
|
||||
$this->validatorService->shouldReceive('setUserLevel->handle')->once()->andReturn(collect([]));
|
||||
$this->repository->shouldReceive('isUniqueUuidCombo')
|
||||
->once()
|
||||
->with($this->getKnownUuid(), substr($this->getKnownUuid(), 0, 8))
|
||||
->andReturn(true);
|
||||
|
||||
$this->repository->shouldReceive('create')->with(m::subset([
|
||||
'uuid' => $this->getKnownUuid(),
|
||||
'uuidShort' => substr($this->getKnownUuid(), 0, 8),
|
||||
'node_id' => $model->node_id,
|
||||
'allocation_id' => $model->allocation_id,
|
||||
'nest_id' => $model->nest_id,
|
||||
'egg_id' => $model->egg_id,
|
||||
]))->andReturn($model);
|
||||
|
||||
$this->allocationRepository->shouldReceive('assignAllocationsToServer')->once()->with($model->id, [$model->allocation_id]);
|
||||
$this->configurationStructureService->shouldReceive('handle')->once()->with($model)->andReturn([]);
|
||||
|
||||
$this->daemonServerRepository->shouldReceive('setServer->create')->once();
|
||||
$this->connection->shouldReceive('commit')->once()->withNoArgs()->andReturnNull();
|
||||
|
||||
$this->getService()->handle(
|
||||
collect($model->toArray())->except(['node_id', 'nest_id'])->toArray()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an auto-deployment object is used correctly if passed.
|
||||
*/
|
||||
public function testAutoDeploymentObject()
|
||||
{
|
||||
$model = factory(Server::class)->make(['uuid' => $this->getKnownUuid()]);
|
||||
$deploymentObject = new DeploymentObject();
|
||||
$deploymentObject->setPorts(['25565']);
|
||||
$deploymentObject->setDedicated(false);
|
||||
$deploymentObject->setLocations([1]);
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->once()->withNoArgs();
|
||||
|
||||
$this->findViableNodesService->shouldReceive('setLocations')->once()->with($deploymentObject->getLocations())->andReturnSelf();
|
||||
$this->findViableNodesService->shouldReceive('setDisk')->once()->with($model->disk)->andReturnSelf();
|
||||
$this->findViableNodesService->shouldReceive('setMemory')->once()->with($model->memory)->andReturnSelf();
|
||||
$this->findViableNodesService->shouldReceive('handle')->once()->withNoArgs()->andReturn([1, 2]);
|
||||
|
||||
$allocationModel = factory(Allocation::class)->make([
|
||||
'id' => $model->allocation_id,
|
||||
'node_id' => $model->node_id,
|
||||
]);
|
||||
$this->allocationSelectionService->shouldReceive('setDedicated')->once()->with($deploymentObject->isDedicated())->andReturnSelf();
|
||||
$this->allocationSelectionService->shouldReceive('setNodes')->once()->with([1, 2])->andReturnSelf();
|
||||
$this->allocationSelectionService->shouldReceive('setPorts')->once()->with($deploymentObject->getPorts())->andReturnSelf();
|
||||
$this->allocationSelectionService->shouldReceive('handle')->once()->withNoArgs()->andReturn($allocationModel);
|
||||
|
||||
$this->validatorService->shouldReceive('setUserLevel->handle')->once()->andReturn(collect([]));
|
||||
$this->repository->shouldReceive('isUniqueUuidCombo')
|
||||
->once()
|
||||
->with($this->getKnownUuid(), substr($this->getKnownUuid(), 0, 8))
|
||||
->andReturn(true);
|
||||
|
||||
$this->repository->shouldReceive('create')->with(m::subset([
|
||||
'uuid' => $this->getKnownUuid(),
|
||||
'uuidShort' => substr($this->getKnownUuid(), 0, 8),
|
||||
'node_id' => $model->node_id,
|
||||
'allocation_id' => $model->allocation_id,
|
||||
'nest_id' => $model->nest_id,
|
||||
'egg_id' => $model->egg_id,
|
||||
]))->andReturn($model);
|
||||
|
||||
$this->allocationRepository->shouldReceive('assignAllocationsToServer')->once()->with($model->id, [$model->allocation_id]);
|
||||
$this->configurationStructureService->shouldReceive('handle')->once()->with($model)->andReturn([]);
|
||||
|
||||
$this->daemonServerRepository->shouldReceive('setServer->create')->once();
|
||||
$this->connection->shouldReceive('commit')->once()->withNoArgs()->andReturnNull();
|
||||
|
||||
$this->getService()->handle(
|
||||
collect($model->toArray())->except(['allocation_id', 'node_id'])->toArray(), $deploymentObject
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test handling of node timeout or other daemon error.
|
||||
*/
|
||||
public function testExceptionShouldBeThrownIfTheRequestFails()
|
||||
{
|
||||
$this->expectException(DaemonConnectionException::class);
|
||||
|
||||
$model = factory(Server::class)->make([
|
||||
'uuid' => $this->getKnownUuid(),
|
||||
]);
|
||||
|
||||
$this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull();
|
||||
$this->repository->shouldReceive('isUniqueUuidCombo')->once()->andReturn(true);
|
||||
$this->repository->shouldReceive('create')->once()->andReturn($model);
|
||||
$this->allocationRepository->shouldReceive('assignAllocationsToServer')->once()->andReturn(1);
|
||||
$this->validatorService->shouldReceive('setUserLevel')->once()->andReturnSelf();
|
||||
$this->validatorService->shouldReceive('handle')->once()->andReturn(collect([]));
|
||||
$this->configurationStructureService->shouldReceive('handle')->once()->andReturn([]);
|
||||
|
||||
$this->connection->expects('commit')->withNoArgs();
|
||||
|
||||
$this->daemonServerRepository->shouldReceive('setServer')->with($model)->once()->andThrow(
|
||||
new DaemonConnectionException(
|
||||
new ConnectException('', new Request('GET', 'test'))
|
||||
)
|
||||
);
|
||||
|
||||
$this->serverDeletionService->expects('withForce')->with(true)->andReturnSelf();
|
||||
$this->serverDeletionService->expects('handle')->with($model);
|
||||
|
||||
$this->getService()->handle($model->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance of the service with mocked dependencies.
|
||||
*
|
||||
* @return \Pterodactyl\Services\Servers\ServerCreationService
|
||||
*/
|
||||
private function getService(): ServerCreationService
|
||||
{
|
||||
return new ServerCreationService(
|
||||
$this->allocationRepository,
|
||||
$this->allocationSelectionService,
|
||||
$this->connection,
|
||||
$this->daemonServerRepository,
|
||||
$this->eggRepository,
|
||||
$this->findViableNodesService,
|
||||
$this->configurationStructureService,
|
||||
$this->serverDeletionService,
|
||||
$this->repository,
|
||||
$this->serverVariableRepository,
|
||||
$this->validatorService
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue