2021-06-26 11:23:15 -04:00
< ? php
2018-04-14 13:47:13 -04:00
2021-06-26 11:23:15 -04:00
namespace Tests ;
2022-07-12 14:38:11 -04:00
use BookStack\Auth\Permissions\JointPermissionBuilder ;
2022-07-12 15:15:41 -04:00
use BookStack\Auth\Permissions\PermissionApplicator ;
2021-06-26 11:23:15 -04:00
use BookStack\Auth\Permissions\PermissionsRepo ;
2021-08-28 16:48:17 -04:00
use BookStack\Auth\Permissions\RolePermission ;
2021-06-26 11:23:15 -04:00
use BookStack\Auth\Role ;
2019-10-05 07:55:01 -04:00
use BookStack\Auth\User ;
2020-11-21 19:17:45 -05:00
use BookStack\Entities\Models\Book ;
use BookStack\Entities\Models\Bookshelf ;
use BookStack\Entities\Models\Chapter ;
use BookStack\Entities\Models\Entity ;
use BookStack\Entities\Models\Page ;
2019-10-05 07:55:01 -04:00
use BookStack\Entities\Repos\BookRepo ;
use BookStack\Entities\Repos\BookshelfRepo ;
use BookStack\Entities\Repos\ChapterRepo ;
2018-10-13 06:27:55 -04:00
use BookStack\Entities\Repos\PageRepo ;
2018-09-25 07:30:50 -04:00
use BookStack\Settings\SettingService ;
2019-07-17 17:36:49 -04:00
use BookStack\Uploads\HttpFetcher ;
2021-10-13 11:51:27 -04:00
use GuzzleHttp\Client ;
use GuzzleHttp\Handler\MockHandler ;
use GuzzleHttp\HandlerStack ;
use GuzzleHttp\Middleware ;
2021-08-28 16:48:17 -04:00
use Illuminate\Http\JsonResponse ;
2019-09-14 09:12:39 -04:00
use Illuminate\Support\Env ;
2020-05-23 06:26:48 -04:00
use Illuminate\Support\Facades\Log ;
2021-10-26 17:04:18 -04:00
use Illuminate\Testing\Assert as PHPUnit ;
2019-10-05 07:55:01 -04:00
use Mockery ;
2020-05-23 06:26:48 -04:00
use Monolog\Handler\TestHandler ;
use Monolog\Logger ;
2021-10-13 11:51:27 -04:00
use Psr\Http\Client\ClientInterface ;
2018-04-14 13:47:13 -04:00
trait SharedTestHelpers
{
protected $admin ;
protected $editor ;
/**
* Set the current user context to be an admin .
*/
public function asAdmin ()
{
return $this -> actingAs ( $this -> getAdmin ());
}
/**
* Get the current admin user .
*/
2021-03-03 17:11:00 -05:00
public function getAdmin () : User
{
if ( is_null ( $this -> admin )) {
2018-04-14 13:47:13 -04:00
$adminRole = Role :: getSystemRole ( 'admin' );
$this -> admin = $adminRole -> users -> first ();
}
2021-03-03 17:11:00 -05:00
2018-04-14 13:47:13 -04:00
return $this -> admin ;
}
/**
* Set the current user context to be an editor .
*/
public function asEditor ()
{
return $this -> actingAs ( $this -> getEditor ());
}
/**
* Get a editor user .
*/
2021-03-03 17:11:00 -05:00
protected function getEditor () : User
{
if ( $this -> editor === null ) {
2018-04-14 13:47:13 -04:00
$editorRole = Role :: getRole ( 'editor' );
$this -> editor = $editorRole -> users -> first ();
}
2021-06-26 11:23:15 -04:00
2018-04-14 13:47:13 -04:00
return $this -> editor ;
}
/**
2020-05-23 06:26:48 -04:00
* Get an instance of a user with 'viewer' permissions .
2018-04-14 13:47:13 -04:00
*/
2020-05-23 06:26:48 -04:00
protected function getViewer ( array $attributes = []) : User
2018-04-14 13:47:13 -04:00
{
2019-10-05 07:55:01 -04:00
$user = Role :: getRole ( 'viewer' ) -> users () -> first ();
2020-05-23 06:26:48 -04:00
if ( ! empty ( $attributes )) {
$user -> forceFill ( $attributes ) -> save ();
}
2021-06-26 11:23:15 -04:00
2018-04-14 13:47:13 -04:00
return $user ;
}
2021-05-29 18:42:21 -04:00
/**
* Get a user that ' s not a system user such as the guest user .
*/
2021-09-17 18:44:54 -04:00
public function getNormalUser () : User
2021-05-29 18:42:21 -04:00
{
return User :: query () -> where ( 'system_name' , '=' , null ) -> get () -> last ();
}
2018-09-21 10:15:16 -04:00
/**
* Regenerate the permission for an entity .
*/
2021-03-03 17:11:00 -05:00
protected function regenEntityPermissions ( Entity $entity ) : void
2018-09-21 10:15:16 -04:00
{
2019-09-19 19:18:28 -04:00
$entity -> rebuildPermissions ();
2018-09-21 10:15:16 -04:00
$entity -> load ( 'jointPermissions' );
}
/**
* Create and return a new bookshelf .
*/
2021-03-03 17:11:00 -05:00
public function newShelf ( array $input = [ 'name' => 'test shelf' , 'description' => 'My new test shelf' ]) : Bookshelf
{
2019-10-05 07:55:01 -04:00
return app ( BookshelfRepo :: class ) -> create ( $input , []);
2018-09-21 10:15:16 -04:00
}
2018-04-14 13:47:13 -04:00
/**
* Create and return a new book .
*/
2021-03-03 17:11:00 -05:00
public function newBook ( array $input = [ 'name' => 'test book' , 'description' => 'My new test book' ]) : Book
{
2019-10-05 07:55:01 -04:00
return app ( BookRepo :: class ) -> create ( $input );
2018-04-14 13:47:13 -04:00
}
/**
2021-06-26 11:23:15 -04:00
* Create and return a new test chapter .
2018-04-14 13:47:13 -04:00
*/
2021-10-26 17:04:18 -04:00
public function newChapter ( array $input , Book $book ) : Chapter
2021-03-03 17:11:00 -05:00
{
2019-10-05 07:55:01 -04:00
return app ( ChapterRepo :: class ) -> create ( $input , $book );
2018-04-14 13:47:13 -04:00
}
/**
2021-06-26 11:23:15 -04:00
* Create and return a new test page .
2018-04-14 13:47:13 -04:00
*/
2021-03-03 17:11:00 -05:00
public function newPage ( array $input = [ 'name' => 'test page' , 'html' => 'My new test page' ]) : Page
{
$book = Book :: query () -> first ();
2018-10-13 06:27:55 -04:00
$pageRepo = app ( PageRepo :: class );
2019-10-05 07:55:01 -04:00
$draftPage = $pageRepo -> getNewDraftPage ( $book );
2021-06-26 11:23:15 -04:00
2019-10-05 07:55:01 -04:00
return $pageRepo -> publishDraft ( $draftPage , $input );
2018-04-14 13:47:13 -04:00
}
/**
* Quickly sets an array of settings .
*/
2021-03-03 17:11:00 -05:00
protected function setSettings ( array $settingsArray ) : void
2018-04-14 13:47:13 -04:00
{
$settings = app ( SettingService :: class );
foreach ( $settingsArray as $key => $value ) {
$settings -> put ( $key , $value );
}
}
/**
* Manually set some permissions on an entity .
*/
2021-03-03 17:11:00 -05:00
protected function setEntityRestrictions ( Entity $entity , array $actions = [], array $roles = []) : void
2018-04-14 13:47:13 -04:00
{
$entity -> restricted = true ;
$entity -> permissions () -> delete ();
$permissions = [];
foreach ( $actions as $action ) {
foreach ( $roles as $role ) {
$permissions [] = [
'role_id' => $role -> id ,
2021-06-26 11:23:15 -04:00
'action' => strtolower ( $action ),
2018-04-14 13:47:13 -04:00
];
}
}
$entity -> permissions () -> createMany ( $permissions );
$entity -> save ();
$entity -> load ( 'permissions' );
2022-07-12 15:15:41 -04:00
$this -> app -> make ( JointPermissionBuilder :: class ) -> rebuildForEntity ( $entity );
2018-04-14 13:47:13 -04:00
$entity -> load ( 'jointPermissions' );
}
2018-09-21 10:15:16 -04:00
/**
* Give the given user some permissions .
*/
2021-03-03 17:11:00 -05:00
protected function giveUserPermissions ( User $user , array $permissions = []) : void
2018-09-21 10:15:16 -04:00
{
$newRole = $this -> createNewRole ( $permissions );
$user -> attachRole ( $newRole );
$user -> load ( 'roles' );
2021-01-01 20:22:41 -05:00
$user -> clearPermissionCache ();
2018-09-21 10:15:16 -04:00
}
2021-08-28 16:48:17 -04:00
/**
* Completely remove the given permission name from the given user .
*/
2022-06-19 13:12:36 -04:00
protected function removePermissionFromUser ( User $user , string $permissionName )
2021-08-28 16:48:17 -04:00
{
2022-07-12 14:38:11 -04:00
$permissionBuilder = app () -> make ( JointPermissionBuilder :: class );
2022-06-19 13:12:36 -04:00
/** @var RolePermission $permission */
$permission = RolePermission :: query () -> where ( 'name' , '=' , $permissionName ) -> firstOrFail ();
2022-06-19 13:14:53 -04:00
$roles = $user -> roles () -> whereHas ( 'permissions' , function ( $query ) use ( $permission ) {
2022-06-19 13:12:36 -04:00
$query -> where ( 'id' , '=' , $permission -> id );
}) -> get ();
2021-08-28 16:48:17 -04:00
/** @var Role $role */
2022-06-19 13:12:36 -04:00
foreach ( $roles as $role ) {
2021-08-28 16:48:17 -04:00
$role -> detachPermission ( $permission );
2022-07-12 15:15:41 -04:00
$permissionBuilder -> rebuildForRole ( $role );
2021-08-28 16:48:17 -04:00
}
2022-06-19 13:12:36 -04:00
2021-08-28 16:48:17 -04:00
$user -> clearPermissionCache ();
}
2018-09-21 10:15:16 -04:00
/**
* Create a new basic role for testing purposes .
*/
2021-03-03 17:11:00 -05:00
protected function createNewRole ( array $permissions = []) : Role
2018-09-21 10:15:16 -04:00
{
$permissionRepo = app ( PermissionsRepo :: class );
2021-10-30 16:29:59 -04:00
$roleData = Role :: factory () -> make () -> toArray ();
2018-09-21 10:15:16 -04:00
$roleData [ 'permissions' ] = array_flip ( $permissions );
2021-06-26 11:23:15 -04:00
2018-09-21 10:15:16 -04:00
return $permissionRepo -> saveNewRole ( $roleData );
}
2021-09-13 17:54:21 -04:00
/**
* Create a group of entities that belong to a specific user .
2021-09-18 16:21:44 -04:00
*
2021-09-15 17:18:37 -04:00
* @ return array { book : Book , chapter : Chapter , page : Page }
2021-09-13 17:54:21 -04:00
*/
protected function createEntityChainBelongingToUser ( User $creatorUser , ? User $updaterUser = null ) : array
{
if ( empty ( $updaterUser )) {
$updaterUser = $creatorUser ;
}
$userAttrs = [ 'created_by' => $creatorUser -> id , 'owned_by' => $creatorUser -> id , 'updated_by' => $updaterUser -> id ];
2021-10-30 16:29:59 -04:00
$book = Book :: factory () -> create ( $userAttrs );
$chapter = Chapter :: factory () -> create ( array_merge ([ 'book_id' => $book -> id ], $userAttrs ));
$page = Page :: factory () -> create ( array_merge ([ 'book_id' => $book -> id , 'chapter_id' => $chapter -> id ], $userAttrs ));
2022-07-12 14:38:11 -04:00
2022-07-12 15:15:41 -04:00
$this -> app -> make ( JointPermissionBuilder :: class ) -> rebuildForEntity ( $book );
2021-09-13 17:54:21 -04:00
return compact ( 'book' , 'chapter' , 'page' );
}
2019-07-17 17:36:49 -04:00
/**
* Mock the HttpFetcher service and return the given data on fetch .
*/
protected function mockHttpFetch ( $returnData , int $times = 1 )
{
2019-10-05 07:55:01 -04:00
$mockHttp = Mockery :: mock ( HttpFetcher :: class );
2019-07-17 17:36:49 -04:00
$this -> app [ HttpFetcher :: class ] = $mockHttp ;
$mockHttp -> shouldReceive ( 'fetch' )
-> times ( $times )
-> andReturn ( $returnData );
}
2021-10-13 11:51:27 -04:00
/**
* Mock the http client used in BookStack .
* Returns a reference to the container which holds all history of http transactions .
2021-10-16 11:01:59 -04:00
*
2021-10-13 11:51:27 -04:00
* @ link https :// docs . guzzlephp . org / en / stable / testing . html #history-middleware
*/
protected function & mockHttpClient ( array $responses = []) : array
{
$container = [];
$history = Middleware :: history ( $container );
$mock = new MockHandler ( $responses );
$handlerStack = new HandlerStack ( $mock );
$handlerStack -> push ( $history );
$this -> app [ ClientInterface :: class ] = new Client ([ 'handler' => $handlerStack ]);
2021-10-16 11:01:59 -04:00
2021-10-13 11:51:27 -04:00
return $container ;
}
2019-09-14 09:12:39 -04:00
/**
* Run a set test with the given env variable .
* Remembers the original and resets the value after test .
*/
protected function runWithEnv ( string $name , $value , callable $callback )
{
Env :: disablePutenv ();
2019-09-19 20:18:59 -04:00
$originalVal = $_SERVER [ $name ] ? ? null ;
2019-09-14 09:12:39 -04:00
if ( is_null ( $value )) {
unset ( $_SERVER [ $name ]);
} else {
$_SERVER [ $name ] = $value ;
}
$this -> refreshApplication ();
$callback ();
if ( is_null ( $originalVal )) {
unset ( $_SERVER [ $name ]);
} else {
$_SERVER [ $name ] = $originalVal ;
}
}
/**
* Check the keys and properties in the given map to include
* exist , albeit not exclusively , within the map to check .
*/
2021-03-03 17:11:00 -05:00
protected function assertArrayMapIncludes ( array $mapToInclude , array $mapToCheck , string $message = '' ) : void
2019-09-14 09:12:39 -04:00
{
$passed = true ;
foreach ( $mapToInclude as $key => $value ) {
if ( ! isset ( $mapToCheck [ $key ]) || $mapToCheck [ $key ] !== $mapToInclude [ $key ]) {
$passed = false ;
}
}
$toIncludeStr = print_r ( $mapToInclude , true );
$toCheckStr = print_r ( $mapToCheck , true );
self :: assertThat ( $passed , self :: isTrue (), " Failed asserting that given map: \n \n { $toCheckStr } \n \n includes: \n \n { $toIncludeStr } " );
}
2020-02-02 08:10:21 -05:00
/**
* Assert a permission error has occurred .
*/
protected function assertPermissionError ( $response )
{
2021-06-26 11:23:15 -04:00
PHPUnit :: assertTrue ( $this -> isPermissionError ( $response -> baseResponse ? ? $response -> response ), 'Failed asserting the response contains a permission error.' );
2020-11-06 07:54:39 -05:00
}
2020-02-02 08:10:21 -05:00
2020-11-06 07:54:39 -05:00
/**
* Assert a permission error has occurred .
*/
protected function assertNotPermissionError ( $response )
{
2021-06-26 11:23:15 -04:00
PHPUnit :: assertFalse ( $this -> isPermissionError ( $response -> baseResponse ? ? $response -> response ), 'Failed asserting the response does not contain a permission error.' );
2020-11-06 07:54:39 -05:00
}
/**
* Check if the given response is a permission error .
*/
private function isPermissionError ( $response ) : bool
{
return $response -> status () === 302
2021-08-28 16:48:17 -04:00
&& (
(
$response -> headers -> get ( 'Location' ) === url ( '/' )
&& strpos ( session () -> pull ( 'error' , '' ), 'You do not have permission to access' ) === 0
)
||
(
$response instanceof JsonResponse &&
$response -> json ([ 'error' => 'You do not have permission to perform the requested action.' ])
)
);
2020-02-02 08:10:21 -05:00
}
2021-10-13 11:51:27 -04:00
/**
* Assert that the session has a particular error notification message set .
*/
protected function assertSessionError ( string $message )
{
$error = session () -> get ( 'error' );
PHPUnit :: assertTrue ( $error === $message , " Failed asserting the session contains an error. \n Found: { $error } \n Expecting: { $message } " );
}
2020-05-23 06:26:48 -04:00
/**
* Set a test handler as the logging interface for the application .
* Allows capture of logs for checking against during tests .
*/
protected function withTestLogger () : TestHandler
{
$monolog = new Logger ( 'testing' );
$testHandler = new TestHandler ();
$monolog -> pushHandler ( $testHandler );
2021-03-03 17:11:00 -05:00
Log :: extend ( 'testing' , function () use ( $monolog ) {
2020-05-23 06:26:48 -04:00
return $monolog ;
});
Log :: setDefaultDriver ( 'testing' );
return $testHandler ;
}
2021-06-26 11:23:15 -04:00
}