diff --git a/app/Exceptions/DisplayException.php b/app/Exceptions/DisplayException.php index 80c5771a5..aa18a1c1b 100644 --- a/app/Exceptions/DisplayException.php +++ b/app/Exceptions/DisplayException.php @@ -28,9 +28,9 @@ class DisplayException extends PterodactylException * @param string $message * @param Throwable|null $previous * @param string $level - * @internal param mixed $log + * @param int $code */ - public function __construct($message, Throwable $previous = null, $level = self::LEVEL_ERROR) + public function __construct($message, Throwable $previous = null, $level = self::LEVEL_ERROR, $code = 0) { $this->level = $level; @@ -38,7 +38,7 @@ class DisplayException extends PterodactylException Log::{$level}($previous); } - parent::__construct($message); + parent::__construct($message, $code, $previous); } /** diff --git a/tests/Traits/MocksRequestException.php b/tests/Traits/MocksRequestException.php new file mode 100644 index 000000000..81e0e5414 --- /dev/null +++ b/tests/Traits/MocksRequestException.php @@ -0,0 +1,49 @@ +getExceptionMock()->shouldReceive('getResponse')->andReturn($this->exceptionResponse); + } + + /** + * Return a mocked instance of the request exception. + * + * @return \Mockery\MockInterface + */ + private function getExceptionMock(): MockInterface + { + return $this->exception ?? $this->exception = Mockery::mock(RequestException::class); + } + + /** + * Set the exception response. + * + * @param mixed $response + */ + protected function setExceptionResponse($response) + { + $this->exceptionResponse = $response; + } +} diff --git a/tests/Unit/Services/Allocations/SetDefaultAllocationServiceTest.php b/tests/Unit/Services/Allocations/SetDefaultAllocationServiceTest.php new file mode 100644 index 000000000..72a837e78 --- /dev/null +++ b/tests/Unit/Services/Allocations/SetDefaultAllocationServiceTest.php @@ -0,0 +1,156 @@ +connection = m::mock(ConnectionInterface::class); + $this->daemonRepository = m::mock(DaemonRepositoryInterface::class); + $this->repository = m::mock(AllocationRepositoryInterface::class); + $this->serverRepository = m::mock(ServerRepositoryInterface::class); + } + + /** + * Test that an allocation can be updated. + * + * @dataProvider useModelDataProvider + */ + public function testAllocationIsUpdated(bool $useModel) + { + $allocations = factory(Allocation::class)->times(2)->make(); + $model = factory(Server::class)->make(); + if (! $useModel) { + $this->serverRepository->shouldReceive('find')->with(1234)->once()->andReturn($model); + } + + $this->repository->shouldReceive('findWhere')->with([['server_id', '=', $model->id]])->once()->andReturn($allocations); + $this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); + $this->serverRepository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf(); + $this->serverRepository->shouldReceive('update')->with($model->id, [ + 'allocation_id' => $allocations->first()->id, + ])->once()->andReturnNull(); + + $this->daemonRepository->shouldReceive('setAccessServer')->with($model->uuid)->once()->andReturnSelf(); + $this->daemonRepository->shouldReceive('setNode')->with($model->node_id)->once()->andReturnSelf(); + $this->daemonRepository->shouldReceive('update')->with([ + 'build' => [ + 'default' => [ + 'ip' => $allocations->first()->ip, + 'port' => $allocations->first()->port, + ], + 'ports|overwrite' => $allocations->groupBy('ip')->map(function ($item) { + return $item->pluck('port'); + })->toArray(), + ], + ])->once()->andReturnNull(); + $this->connection->shouldReceive('commit')->withNoArgs()->once()->andReturnNull(); + + $response = $this->getService()->handle($useModel ? $model : 1234, $allocations->first()->id); + $this->assertNotEmpty($response); + $this->assertSame($allocations->first(), $response); + } + + /** + * Test that an allocation that doesn't belong to a server throws an exception. + * + * @expectedException \Pterodactyl\Exceptions\Service\Allocation\AllocationDoesNotBelongToServerException + */ + public function testAllocationNotBelongingToServerThrowsException() + { + $model = factory(Server::class)->make(); + $this->repository->shouldReceive('findWhere')->with([['server_id', '=', $model->id]])->once()->andReturn(collect()); + + $this->getService()->handle($model, 1234); + } + + /** + * Test that an exception thrown by guzzle is handled properly. + */ + public function testExceptionThrownByGuzzleIsHandled() + { + $this->configureExceptionMock(); + + $allocation = factory(Allocation::class)->make(); + $model = factory(Server::class)->make(); + + $this->repository->shouldReceive('findWhere')->with([['server_id', '=', $model->id]])->once()->andReturn(collect([$allocation])); + $this->connection->shouldReceive('beginTransaction')->withNoArgs()->once()->andReturnNull(); + $this->serverRepository->shouldReceive('withoutFresh')->withNoArgs()->once()->andReturnSelf(); + $this->serverRepository->shouldReceive('update')->with($model->id, [ + 'allocation_id' => $allocation->id, + ])->once()->andReturnNull(); + + $this->daemonRepository->shouldReceive('setAccessServer->setNode->update')->once()->andThrow($this->getExceptionMock()); + $this->connection->shouldReceive('rollBack')->withNoArgs()->once()->andReturnNull(); + + try { + $this->getService()->handle($model, $allocation->id); + } catch (PterodactylException $exception) { + $this->assertInstanceOf(DaemonConnectionException::class, $exception); + $this->assertInstanceOf(RequestException::class, $exception->getPrevious()); + } + } + + /** + * 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\Allocations\SetDefaultAllocationService + */ + private function getService(): SetDefaultAllocationService + { + return new SetDefaultAllocationService($this->repository, $this->connection, $this->daemonRepository, $this->serverRepository); + } +} diff --git a/tests/Unit/Services/Databases/DatabasePasswordServiceTest.php b/tests/Unit/Services/Databases/DatabasePasswordServiceTest.php index 099d44616..54d46b950 100644 --- a/tests/Unit/Services/Databases/DatabasePasswordServiceTest.php +++ b/tests/Unit/Services/Databases/DatabasePasswordServiceTest.php @@ -51,7 +51,7 @@ class DatabasePasswordServiceTest extends TestCase * * @dataProvider useModelDataProvider */ - public function testPasswordIsChanged($useModel) + public function testPasswordIsChanged(bool $useModel) { $model = factory(Database::class)->make(); diff --git a/tests/Unit/Services/Databases/Hosts/HostDeletionServiceTest.php b/tests/Unit/Services/Databases/Hosts/HostDeletionServiceTest.php index 402bf507c..bd927b8e8 100644 --- a/tests/Unit/Services/Databases/Hosts/HostDeletionServiceTest.php +++ b/tests/Unit/Services/Databases/Hosts/HostDeletionServiceTest.php @@ -51,7 +51,7 @@ class HostDeletionServiceTest extends TestCase * * @dataProvider databaseCountDataProvider */ - public function testExceptionIsThrownIfDeletingHostWithDatabases($count) + public function testExceptionIsThrownIfDeletingHostWithDatabases(int $count) { $this->databaseRepository->shouldReceive('findCountWhere')->with([['database_host_id', '=', 1234]])->once()->andReturn($count);