src/cases/NgRestTestCase.php
<?php
namespace luya\testsuite\cases;
use yii\base\InvalidConfigException;
use yii\db\Exception as DbException;
use luya\testsuite\fixtures\ActiveRecordFixture;
use luya\base\Boot;
use luya\helpers\ArrayHelper;
use luya\testsuite\fixtures\NgRestModelFixture;
use luya\testsuite\traits\AdminDatabaseTableTrait;
use yii\base\Controller;
/**
* NgRest Test Case.
*
* The NgRestTestCase extends the {{luya\testsuite\fixture\NgRestModelFixture}} by auto setup the right
* Database connection and allows you to test main components Model, API and Controller very easy.
*
* The API and Controller tests are optional, this means you don't have to provide {{$apiClass}} or
* {{$controllerClass}} in order to setup the test case correctly. The basic tests is just doing
* some basic execution test to see if properties and methods does have values and does not return any
* php exception, parse or runtime error.
*
* With this test case you can easy access the Model, API and Controller object in order to test your
* custom functionality.
*
* ```php
* public function testSomeCustomFunctions()
* {
* // accessing the controller object
* $this->assertSame('FooBar', $this->controller->actionFooBar()); // runs the action method `actionFooBar`
*
* // accessing the api object
* $this->assertSame('FooBar', $this->api->actionFooBar()); // runs the action method `actionFooBar`
* }
* ```
*
* Full example usage and definition:
*
* ```php
* class NgRestTestCaseTest extends NgRestTestCase
* {
* public $modelClass = 'luya\testsuite\tests\data\NgRestTestModel';
*
* public $apiClass = 'luya\testsuite\tests\data\NgRestTestApi';
*
* public $controllerClass = 'luya\testsuite\tests\data\NgRestTestController';
*
* public $modelFixtureData = [
* 'model1' => [
* 'id' => 1,
* 'user_id' => 1,
* 'group_id' => 1,
* ],
* ];
*
* public function getConfigArray()
* {
* return [
* 'id' => 'ngresttest',
* 'basePath' => dirname(__DIR__),
* ];
* }
*
* public function testAssertion()
* {
* $model1 = $this->modelFixture->getModel('model1');
* $this->assertSame(1, $model1->id);
*
* // do inserts, updates or deletions with the model
* // $model = $this->modelFixture->newModel;
* }
* }
* ```
*
* How to call an API Endpoint:
*
* ```php
* public function testMyEndpoints()
* {
* // test a custom api endpoint (action) with auth checks
* $this->runControllerAction($this->api, 'test'); // where test is the action name.
*
* // or you can access this action directly without auth checks.
* $this->api->actionTest();
*
* }
* ```
*
* @author Basil Suter <basil@nadar.io>
* @since 1.0.10
*/
abstract class NgRestTestCase extends WebApplicationTestCase
{
use AdminDatabaseTableTrait;
/**
* @const int
*
* @since 1.0.14
*/
const ID_AUTH_API = 1;
/**
* @const int
*
* @since 1.0.14
*/
const ID_AUTH_CONTROLLER = 2;
/**
* @const int
*
* @since 1.0.14
*/
const ID_USER_TESTER = 1;
/**
* @const int
*
* @since 1.0.14
*/
const ID_GROUP_TESTER = 1;
/**
* @const int
*
* @since 1.0.14
*/
const ID_GROUP_AUTH_API = 1;
/**
* @const int
*
* @since 1.0.14
*/
const ID_GROUP_AUTH_CONTROLLER = 2;
/**
* @var string The path to the ngrest model.
*/
public $modelClass;
/**
* @var array An array with fixture data for the model.
*/
public $modelFixtureData = [];
/**
* @var array An array with schema defintion for the fixture model.
*/
public $modelSchema = [];
/**
* @var string The path to the ngrest api.
*/
public $apiClass;
/**
* @var string The path to the ngrest controller.
*/
public $controllerClass;
/**
* @var \luya\admin\ngrest\base\Api
*/
protected $api;
/**
* @var Controller
*/
protected $controller;
/**
* @var string
* @since 1.0.14
*/
protected $controllerId;
/**
* @var NgRestModelFixture
*/
protected $modelFixture;
/**
* @var NgRestModelFixture
*/
protected $userFixture;
/**
* @var ActiveRecordFixture
*/
protected $userOnlineFixture;
/**
* @var ActiveRecordFixture
*/
protected $userGroupFixture;
/**
* @var ActiveRecordFixture
*/
protected $ngrestLogFixture;
protected $langFixture;
public function afterSetup()
{
parent::afterSetup();
if (!$this->modelClass) {
throw new InvalidConfigException("The 'modelClass' property can not be empty.");
}
$this->modelFixture = new ActiveRecordFixture([
'modelClass' => $this->modelClass,
'fixtureData' => $this->modelFixtureData,
'schema' => $this->modelSchema,
]);
$this->mockBasicAdminModels();
if ($this->apiClass) {
$class = $this->apiClass;
$modelClass = $this->modelClass;
$this->api = new $class($modelClass::ngRestApiEndpoint(), $this->app);
}
if ($this->controllerClass) {
$class = $this->controllerClass;
$className = preg_replace('/^.*\\\/', '', $class::className());
$this->controllerId = strtolower(str_replace('Controller', '', $className));
$this->controller = new $class($this->controllerId, $this->app);
}
}
/**
* Basic admin module env model mocking.
*/
protected function mockBasicAdminModels()
{
$this->createAdminUserGroupTable();
$this->createAdminGroupAuthTable();
$this->createAdminAuthTable();
$this->createAdminUserAuthNotificationTable();
$this->createAdminUserLoginLockoutFixture();
$this->langFixture = $this->createAdminLangFixture([
1 => [
'id' => 1,
'short_code' => 'en',
'name' => 'English',
'is_default' => 1,
'is_deleted' => 0,
]
]);
// user
$this->userFixture = $this->createAdminUserFixture([
'user1' => [
'id' => self::ID_USER_TESTER,
'firstname' => 'John',
'lastname' => 'Doe',
'email' => 'john@example.com',
'is_deleted' => 0,
'is_api_user' => true,
'api_last_activity' => time(),
'auth_token' => 'TestAuthToken',
'is_request_logger_enabled' => false,
]
]);
// user group
$this->userGroupFixture = $this->createAdminGroupFixture(self::ID_GROUP_TESTER);
// login the user
$this->app->adminuser->login($this->userFixture->getModel('user1'));
// user online table
$this->userOnlineFixture = $this->createAdminUserOnlineFixture();
// ngrest logger
$this->ngrestLogFixture = $this->createAdminNgRestLogFixture();
$this->insertRow('admin_user_group', [
'user_id' => self::ID_USER_TESTER,
'group_id' => self::ID_GROUP_TESTER,
]);
$apiEndpoint = $this->modelClass::ngRestApiEndpoint();
list(, , $alias) = explode('-', $apiEndpoint);
$this->insertRow('admin_auth', [
'id' => self::ID_AUTH_API,
'alias_name' => $alias,
'module_name' => $this->app->id,
'is_crud' => 1,
'api' => $apiEndpoint,
]);
$this->insertRow('admin_auth', [
'id' => self::ID_AUTH_CONTROLLER,
'module_name' => $this->app->id,
'is_crud' => 0,
]);
}
/**
* {@inheritDoc}
*/
public function bootApplication(Boot $boot)
{
// ensure the admin module is registered, if not do so.
$config = ArrayHelper::merge([
'modules' => [
'admin' => ['class' => 'luya\admin\Module']
],
'components' => [
'session' => ['class' => 'luya\testsuite\components\DummySession'],
'cache' => ['class' => 'yii\caching\DummyCache'],
'adminuser' => ['class' => 'luya\admin\components\AdminUser', 'enableSession' => false],
'db' => ['class' => 'yii\db\Connection', 'dsn' => 'sqlite::memory:'],
]
], $boot->getConfigArray());
// set the new config.
$boot->setConfigArray($config);
// boot the application
$boot->applicationWeb();
}
/**
* Basic Tests
*/
public function testBasicNgRestMethods()
{
$class = $this->modelClass;
$this->assertNotNull($class::ngRestApiEndpoint());
$this->assertNotNull($class::tableName());
$this->assertTrue(is_array($this->modelFixture->newModel->ngRestAttributeTypes()));
$this->assertNotNull($this->modelFixture->newModel->attributeLabels());
$this->assertTrue(is_array($this->modelFixture->newModel->attributeHints()));
$this->assertTrue(is_array($this->modelFixture->newModel->ngRestScopes()));
$this->assertTrue(is_array($this->modelFixture->newModel->ngRestActiveButtons()));
$this->assertTrue(is_array($this->modelFixture->newModel->ngRestActiveWindows()));
$this->assertTrue(is_array($this->modelFixture->newModel->ngRestRelations()));
if ($this->api) {
$this->assertInstanceOf('luya\admin\ngrest\base\NgRestModel', $this->api->model);
$this->expectException('yii\web\ForbiddenHttpException');
$this->runControllerAction($this->api, 'unlock');
//$this->assertNull($this->api->actionUnlock());
$this->expectException('yii\web\ForbiddenHttpException');
$this->runControllerAction($this->api, 'services');
//$this->api->actionServices();
$this->expectException('yii\web\ForbiddenHttpException');
$this->runControllerAction($this->api, 'search', ['foo']);
//$this->api->actionSearch('foo');
$this->expectException('yii\web\ForbiddenHttpException');
$this->runControllerAction($this->api, 'relation-call', [1, 'foo', 'none']);
//$this->api->actionRelationCall(1, 'foo', 'none');
$this->expectException('yii\web\ForbiddenHttpException');
$this->runControllerAction($this->api, 'filter', ['none']);
//$this->api->actionFilter('none');
$this->expectException('yii\web\ForbiddenHttpException');
$this->runControllerAction($this->api, 'active-window-callback');
//$this->api->actionActiveWindowCallback();
$this->expectException('yii\web\ForbiddenHttpException');
$this->runControllerAction($this->api, 'active-window-render');
//$this->api->actionActiveWindowRender();
$this->expectException('yii\web\ForbiddenHttpException');
$this->runControllerAction($this->api, 'export');
//$this->api->actionExport();
}
if ($this->controller) {
$this->assertInstanceOf('luya\admin\ngrest\base\NgRestModel', $this->controller->getModel());
$this->assertContains('<script>zaa.bootstrap.register', $this->controller->actionIndex());
}
}
/**
* {@inheritDoc}
*/
public function beforeTearDown()
{
parent::beforeTearDown();
$this->modelFixture->cleanup();
$this->userFixture->cleanup();
$this->userGroupFixture->cleanup();
$this->userOnlineFixture->cleanup();
$this->ngrestLogFixture->cleanup();
$this->langFixture->cleanup();
$this->dropAdminAuthTable();
$this->dropAdminGroupAuthTable();
$this->dropAdminUserGroupTable();
$this->dropAdminUserAuthNotificationTable();
}
/**
* Run a certain action insdie a controller, whether its an api or "controller" context.
*
* ```php
* $this->runControllerAction($this->api, 'index');
* ```
*
* Would run the index action of the API Controller.
*
* @param Controller $controller
* @param string $action
* @param array $params
* @param boolean $permission
* @return mixed
* @since 1.0.18
*/
protected function runControllerAction(Controller $controller, $action, array $params = [], $permission = true)
{
$this->app->controller = $controller;
if ($permission) {
$this->setQueryAuthToken();
}
return $controller->runAction($action, $params);
}
/**
* Set the query parameter as auth token.
*
* @param boolean $value
* @param string $token The token to set as query param, if not defined the token from user model user1 will be taken.
* @since 1.0.18
*/
protected function setQueryAuthToken($value = true, $token = null)
{
if ($value) {
$accessToken = $token ? $token : $this->userFixture->getModel('user1')->auth_token;
$this->app->request->setQueryParams(['access-token' => $accessToken]);
} else {
$this->app->request->setQueryParams(['access-token' => null]);
}
}
/**
* Disables api access for test user
*
* @since 1.0.14
*/
protected function removeApiPermissions()
{
$this->app->db->createCommand()->delete('admin_group_auth', ['id' => self::ID_GROUP_AUTH_API])->execute();
}
/**
* Helps to initialize api access permissions
*
* @since 1.0.14
*/
protected function resetApiPermissions($create = false, $update = false, $delete = false)
{
$state = [
'crud_create' => (int)$create,
'crud_update' => (int)$update,
'crud_delete' => (int)$delete,
];
$this->app->db->createCommand()->upsert(
'admin_group_auth',
ArrayHelper::merge([
'id' => self::ID_GROUP_AUTH_API,
'group_id' => self::ID_GROUP_TESTER,
'auth_id' => self::ID_AUTH_API,
], $state),
$state
)->execute();
return $this;
}
/**
* Gives the test user list api permission or removes access
*
* @since 1.0.14
*/
protected function apiCanList($value = true)
{
if (!$value) {
return $this->removeApiPermissions();
}
try {
$this->app->db->createCommand()->insert('admin_group_auth', [
'id' => self::ID_GROUP_AUTH_API,
'group_id' => self::ID_GROUP_TESTER,
'auth_id' => self::ID_AUTH_API,
'crud_create' => 0,
'crud_update' => 0,
'crud_delete' => 0,
])->execute();
} catch (DbException $e) {
// permission is initialized, so having list access already
}
return $this;
}
/**
* Gives the test user create api permission or removes it
*
* @since 1.0.14
*/
protected function apiCanCreate($value = true)
{
$this->app->db->createCommand()->upsert('admin_group_auth', [
'id' => self::ID_GROUP_AUTH_API,
'group_id' => self::ID_GROUP_TESTER,
'auth_id' => self::ID_AUTH_API,
'crud_create' => (int)$value,
'crud_update' => 0,
'crud_delete' => 0,
], [
'crud_create' => (int)$value,
])->execute();
return $this;
}
/**
* Gives the test user update api permission or removes it
*
* @since 1.0.14
*/
protected function apiCanUpdate($value = true)
{
$this->app->db->createCommand()->upsert('admin_group_auth', [
'id' => self::ID_GROUP_AUTH_API,
'group_id' => self::ID_GROUP_TESTER,
'auth_id' => self::ID_AUTH_API,
'crud_create' => 0,
'crud_update' => (int)$value,
'crud_delete' => 0,
], [
'crud_update' => (int)$value,
])->execute();
return $this;
}
/**
* Gives the test user delete api permission or removes it
*
* @since 1.0.14
*/
protected function apiCanDelete($value = true)
{
$this->app->db->createCommand()->upsert('admin_group_auth', [
'id' => self::ID_GROUP_AUTH_API,
'group_id' => self::ID_GROUP_TESTER,
'auth_id' => self::ID_AUTH_API,
'crud_create' => 0,
'crud_update' => 0,
'crud_delete' => (int)$value,
], [
'crud_delete' => (int)$value,
])->execute();
return $this;
}
/**
* Gives the test user access to the controller action
*
* @since 1.0.14
*/
protected function controllerCanAccess($actionId, $value = true)
{
$this->app->db->createCommand()->update('admin_auth', [
'alias_name' => $actionId,
'route' => implode('/', [$this->app->id, $this->controllerId, $actionId]),
], [
'id' => self::ID_AUTH_CONTROLLER
])->execute();
if ($value) {
$this->app->db->createCommand()->insert('admin_group_auth', [
'id' => self::ID_GROUP_AUTH_CONTROLLER,
'group_id' => self::ID_GROUP_TESTER,
'auth_id' => self::ID_AUTH_CONTROLLER,
])->execute();
} else {
$this->app->db->createCommand()->delete('admin_group_auth', [
'id' => self::ID_GROUP_AUTH_CONTROLLER,
])->execute();
}
}
}