2017-08-05 22:20:07 +00:00
< ? php
namespace Tests\Unit\Services\Nodes ;
2020-06-25 04:54:56 +00:00
use Exception ;
2017-08-05 22:20:07 +00:00
use Mockery as m ;
2017-08-05 22:26:30 +00:00
use Tests\TestCase ;
2020-06-25 04:54:56 +00:00
use GuzzleHttp\Psr7\Request ;
2017-08-05 22:20:07 +00:00
use phpmock\phpunit\PHPMock ;
use Pterodactyl\Models\Node ;
2018-01-11 05:19:03 +00:00
use Tests\Traits\MocksRequestException ;
use GuzzleHttp\Exception\ConnectException ;
2020-06-25 04:54:56 +00:00
use GuzzleHttp\Exception\TransferException ;
2018-01-11 05:19:03 +00:00
use Illuminate\Database\ConnectionInterface ;
2020-06-25 04:54:56 +00:00
use Illuminate\Contracts\Encryption\Encrypter ;
2017-08-27 20:10:51 +00:00
use Pterodactyl\Services\Nodes\NodeUpdateService ;
2020-06-25 04:54:56 +00:00
use Pterodactyl\Repositories\Eloquent\NodeRepository ;
use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository ;
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException ;
use Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException ;
2017-08-05 22:20:07 +00:00
2017-08-27 20:10:51 +00:00
class NodeUpdateServiceTest extends TestCase
2017-08-05 22:20:07 +00:00
{
2018-01-11 05:19:03 +00:00
use PHPMock , MocksRequestException ;
2017-08-05 22:20:07 +00:00
/**
2020-06-25 04:54:56 +00:00
* @ var \Mockery\MockInterface
2017-08-05 22:20:07 +00:00
*/
2018-01-11 05:19:03 +00:00
private $connection ;
2017-08-05 22:20:07 +00:00
/**
2020-06-25 04:54:56 +00:00
* @ var \Mockery\MockInterface
2017-08-05 22:20:07 +00:00
*/
2020-06-25 04:54:56 +00:00
private $configurationRepository ;
2017-08-05 22:20:07 +00:00
/**
2020-06-25 04:54:56 +00:00
* @ var \Mockery\MockInterface
*/
private $encrypter ;
/**
* @ var \Mockery\MockInterface
2017-08-05 22:20:07 +00:00
*/
2018-01-11 05:19:03 +00:00
private $repository ;
2017-08-05 22:20:07 +00:00
/**
* Setup tests .
*/
2020-05-09 16:00:52 +00:00
public function setUp () : void
2017-08-05 22:20:07 +00:00
{
parent :: setUp ();
2018-01-11 05:19:03 +00:00
$this -> connection = m :: mock ( ConnectionInterface :: class );
2020-06-25 04:54:56 +00:00
$this -> encrypter = m :: mock ( Encrypter :: class );
$this -> configurationRepository = m :: mock ( DaemonConfigurationRepository :: class );
$this -> repository = m :: mock ( NodeRepository :: class );
2017-08-05 22:20:07 +00:00
}
/**
* Test that the daemon secret is reset when `reset_secret` is passed in the data .
*/
public function testNodeIsUpdatedAndDaemonSecretIsReset ()
{
2020-06-25 04:54:56 +00:00
/** @var \Pterodactyl\Models\Node $model */
$model = factory ( Node :: class ) -> make ([
'fqdn' => 'https://example.com' ,
]);
/** @var \Pterodactyl\Models\Node $updatedModel */
2018-12-02 21:38:59 +00:00
$updatedModel = factory ( Node :: class ) -> make ([
'name' => 'New Name' ,
2020-06-25 04:54:56 +00:00
'fqdn' => 'https://example2.com' ,
2018-12-02 21:38:59 +00:00
]);
2018-01-11 05:19:03 +00:00
2020-06-25 04:54:56 +00:00
$this -> connection -> expects ( 'transaction' ) -> with ( m :: on ( function ( $closure ) use ( $updatedModel ) {
$response = $closure ();
2017-08-05 22:20:07 +00:00
2020-06-25 04:54:56 +00:00
$this -> assertIsArray ( $response );
$this -> assertTrue ( count ( $response ) === 2 );
$this -> assertSame ( $updatedModel , $response [ 0 ]);
$this -> assertFalse ( $response [ 1 ]);
return true ;
})) -> andReturns ([ $updatedModel , false ]);
2017-08-05 22:20:07 +00:00
2020-06-25 04:54:56 +00:00
$this -> encrypter -> expects ( 'encrypt' ) -> with ( m :: on ( function ( $value ) {
return strlen ( $value ) === Node :: DAEMON_TOKEN_LENGTH ;
})) -> andReturns ( 'encrypted_value' );
2018-12-02 21:38:59 +00:00
2020-06-25 04:54:56 +00:00
$this -> repository -> expects ( 'withFreshModel->update' ) -> with ( $model -> id , m :: on ( function ( $value ) {
$this -> assertTrue ( is_array ( $value ));
$this -> assertSame ( 'New Name' , $value [ 'name' ]);
$this -> assertSame ( 'encrypted_value' , $value [ 'daemon_token' ]);
$this -> assertTrue ( strlen ( $value [ 'daemon_token_id' ]) === Node :: DAEMON_TOKEN_ID_LENGTH );
2018-12-02 21:38:59 +00:00
2020-06-25 04:54:56 +00:00
return true ;
}), true , true ) -> andReturns ( $updatedModel );
2018-12-02 21:38:59 +00:00
2020-06-25 04:54:56 +00:00
$this -> configurationRepository -> expects ( 'setNode' ) -> with ( m :: on ( function ( $value ) use ( $model , $updatedModel ) {
$this -> assertInstanceOf ( Node :: class , $value );
$this -> assertSame ( $model -> uuid , $value -> uuid );
2017-08-05 22:20:07 +00:00
2020-06-25 04:54:56 +00:00
// Yes, this is correct. Always use the updated model's FQDN when making requests to
// the Daemon so that any changes to that are properly propagated down to the daemon.
//
// @see https://github.com/pterodactyl/panel/issues/1931
$this -> assertSame ( $updatedModel -> fqdn , $value -> fqdn );
return true ;
})) -> andReturnSelf ();
$this -> configurationRepository -> expects ( 'update' ) -> with ( $updatedModel );
$this -> getService () -> handle ( $model , [
'name' => $updatedModel -> name ,
], true );
2017-08-05 22:20:07 +00:00
}
/**
* Test that daemon secret is not modified when no variable is passed in data .
*/
public function testNodeIsUpdatedAndDaemonSecretIsNotChanged ()
{
2020-06-25 04:54:56 +00:00
/** @var \Pterodactyl\Models\Node $model */
$model = factory ( Node :: class ) -> make ([ 'fqdn' => 'https://example.com' ]);
/** @var \Pterodactyl\Models\Node $updatedModel */
$updatedModel = factory ( Node :: class ) -> make ([ 'name' => 'New Name' , 'fqdn' => $model -> fqdn ]);
$this -> connection -> expects ( 'transaction' ) -> with ( m :: on ( function ( $closure ) use ( $updatedModel ) {
$response = $closure ();
$this -> assertIsArray ( $response );
$this -> assertTrue ( count ( $response ) === 2 );
$this -> assertSame ( $updatedModel , $response [ 0 ]);
$this -> assertFalse ( $response [ 1 ]);
2017-08-05 22:20:07 +00:00
2020-06-25 04:54:56 +00:00
return true ;
})) -> andReturns ([ $updatedModel , false ]);
2018-01-11 05:19:03 +00:00
2020-06-25 04:54:56 +00:00
$this -> repository -> expects ( 'withFreshModel->update' ) -> with ( $model -> id , m :: on ( function ( $value ) {
$this -> assertTrue ( is_array ( $value ));
$this -> assertSame ( 'New Name' , $value [ 'name' ]);
$this -> assertArrayNotHasKey ( 'daemon_token' , $value );
$this -> assertArrayNotHasKey ( 'daemon_token_id' , $value );
2018-01-11 05:19:03 +00:00
2020-06-25 04:54:56 +00:00
return true ;
}), true , true ) -> andReturns ( $updatedModel );
$this -> configurationRepository -> expects ( 'setNode->update' ) -> with ( $updatedModel );
$this -> getService () -> handle ( $model , [ 'name' => $updatedModel -> name ]);
2017-08-05 22:20:07 +00:00
}
/**
2018-01-11 05:19:03 +00:00
* Test that an exception caused by a connection error is handled .
2017-08-05 22:20:07 +00:00
*/
2018-01-11 05:19:03 +00:00
public function testExceptionRelatedToConnection ()
2017-08-05 22:20:07 +00:00
{
2020-06-25 04:54:56 +00:00
$this -> configureExceptionMock ( DaemonConnectionException :: class );
$this -> expectException ( ConfigurationNotPersistedException :: class );
/** @var \Pterodactyl\Models\Node $model */
$model = factory ( Node :: class ) -> make ([ 'fqdn' => 'https://example.com' ]);
/** @var \Pterodactyl\Models\Node $updatedModel */
$updatedModel = factory ( Node :: class ) -> make ([ 'name' => 'New Name' , 'fqdn' => $model -> fqdn ]);
$this -> connection -> expects ( 'transaction' ) -> with ( m :: on ( function ( $closure ) use ( $updatedModel ) {
$response = $closure ();
$this -> assertIsArray ( $response );
$this -> assertTrue ( count ( $response ) === 2 );
$this -> assertSame ( $updatedModel , $response [ 0 ]);
$this -> assertTrue ( $response [ 1 ]);
2018-01-11 05:19:03 +00:00
2020-06-25 04:54:56 +00:00
return true ;
})) -> andReturn ([ $updatedModel , true ]);
2018-01-11 05:19:03 +00:00
2020-06-25 04:54:56 +00:00
$this -> repository -> expects ( 'withFreshModel->update' ) -> with ( $model -> id , m :: on ( function ( $value ) {
$this -> assertTrue ( is_array ( $value ));
$this -> assertSame ( 'New Name' , $value [ 'name' ]);
$this -> assertArrayNotHasKey ( 'daemon_token' , $value );
$this -> assertArrayNotHasKey ( 'daemon_token_id' , $value );
2018-01-11 05:19:03 +00:00
2020-06-25 04:54:56 +00:00
return true ;
}), true , true ) -> andReturns ( $updatedModel );
$this -> configurationRepository -> expects ( 'setNode->update' ) -> with ( $updatedModel ) -> andThrow (
new DaemonConnectionException (
new ConnectException ( '' , new Request ( 'GET' , 'Test' ), new Exception )
)
);
$this -> getService () -> handle ( $model , [ 'name' => $updatedModel -> name ]);
2017-08-05 22:20:07 +00:00
}
/**
2018-01-11 05:19:03 +00:00
* Test that an exception not caused by a daemon connection error is handled .
2017-08-05 22:20:07 +00:00
*/
2018-01-11 05:19:03 +00:00
public function testExceptionNotRelatedToConnection ()
2017-08-05 22:20:07 +00:00
{
2020-06-25 04:54:56 +00:00
/** @var \Pterodactyl\Models\Node $model */
$model = factory ( Node :: class ) -> make ([ 'fqdn' => 'https://example.com' ]);
/** @var \Pterodactyl\Models\Node $updatedModel */
$updatedModel = factory ( Node :: class ) -> make ([ 'name' => 'New Name' , 'fqdn' => $model -> fqdn ]);
$this -> connection -> expects ( 'transaction' ) -> with ( m :: on ( function ( $closure ) use ( $updatedModel ) {
try {
$closure ();
} catch ( Exception $exception ) {
$this -> assertInstanceOf ( DaemonConnectionException :: class , $exception );
$this -> assertSame (
'There was an exception while attempting to communicate with the daemon resulting in a HTTP/E_CONN_REFUSED response code. This exception has been logged.' ,
$exception -> getMessage ()
);
return true ;
}
return false ;
}));
$this -> repository -> expects ( 'withFreshModel->update' ) -> andReturns ( $updatedModel );
$this -> configurationRepository -> expects ( 'setNode->update' ) -> andThrow (
new DaemonConnectionException (
new TransferException ( '' , 500 , new Exception )
)
);
$this -> getService () -> handle ( $model , [ 'name' => $updatedModel -> name ]);
2018-01-11 05:19:03 +00:00
}
2017-08-05 22:20:07 +00:00
2018-01-11 05:19:03 +00:00
/**
* Return an instance of the service with mocked injections .
*
* @ return \Pterodactyl\Services\Nodes\NodeUpdateService
*/
private function getService () : NodeUpdateService
{
2020-06-25 04:54:56 +00:00
return new NodeUpdateService (
$this -> connection , $this -> encrypter , $this -> configurationRepository , $this -> repository
);
2017-08-05 22:20:07 +00:00
}
}