diff --git a/app/modules/api/Controllers/AccountController.php b/app/modules/api/Controllers/AccountController.php index f0d6d9e9..5b7ae16b 100644 --- a/app/modules/api/Controllers/AccountController.php +++ b/app/modules/api/Controllers/AccountController.php @@ -228,7 +228,10 @@ final class AccountController extends ControllerBase $accountRequest->userId = $this->apiService->getParamInt('userId', false, $userData->getId()); $accountRequest->userGroupId = $this->apiService->getParamInt('userGroupId', false, $userData->getUserGroupId()); - $accountRequest->tags = array_map('intval', $this->apiService->getParamArray('tagsId', false, [])); + $accountRequest->tags = array_map( + 'intval', + $this->apiService->getParamArray('tagsId', false, []) + ); $accountRequest->pass = $this->apiService->getParamRaw('pass', true); $this->accountPresetService->checkPasswordPreset($accountRequest); @@ -287,7 +290,10 @@ final class AccountController extends ControllerBase $accountRequest->userGroupId = $this->apiService->getParamInt('userGroupId', false); $accountRequest->userEditId = $this->context->getUserData()->getId(); - $tagsId = array_map('intval', $this->apiService->getParamArray('tagsId', false, [])); + $tagsId = array_map( + 'intval', + $this->apiService->getParamArray('tagsId', false, []) + ); if (count($tagsId) !== 0) { $accountRequest->updateTags = true; @@ -412,9 +418,7 @@ final class AccountController extends ControllerBase } /** - * @throws DependencyException - * @throws NotFoundException - * @throws InvalidClassException + * @throws \SP\Core\Exceptions\InvalidClassException */ protected function initialize(): void { diff --git a/app/modules/api/Controllers/CategoryController.php b/app/modules/api/Controllers/CategoryController.php index 4223ec1d..9f76bee0 100644 --- a/app/modules/api/Controllers/CategoryController.php +++ b/app/modules/api/Controllers/CategoryController.php @@ -24,15 +24,12 @@ namespace SP\Modules\Api\Controllers; -use DI\DependencyException; -use DI\NotFoundException; use Exception; use League\Fractal\Resource\Item; use SP\Adapters\CategoryAdapter; use SP\Core\Acl\ActionsInterface; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; -use SP\Core\Exceptions\InvalidClassException; use SP\DataModel\CategoryData; use SP\DataModel\ItemSearchData; use SP\Modules\Api\Controllers\Help\CategoryHelp; @@ -64,8 +61,6 @@ final class CategoryController extends ControllerBase $id = $this->apiService->getParamInt('id', true); $customFields = Util::boolval($this->apiService->getParamString('customFields')); - $adapter = new CategoryAdapter($this->configData); - $categoryData = $this->categoryService->getById($id); $this->eventDispatcher->notifyEvent( @@ -80,8 +75,10 @@ final class CategoryController extends ControllerBase ); $out = $this->fractal - ->createData( - new Item($categoryData, new CategoryAdapter($this->configData))); + ->createData(new Item( + $categoryData, + new CategoryAdapter($this->configData) + )); if ($customFields) { $this->apiService->requireMasterPass(); @@ -112,6 +109,8 @@ final class CategoryController extends ControllerBase $id = $this->categoryService->create($categoryData); + $categoryData->setId($id); + $this->eventDispatcher->notifyEvent( 'create.category', new Event( @@ -245,9 +244,7 @@ final class CategoryController extends ControllerBase /** * initialize * - * @throws DependencyException - * @throws NotFoundException - * @throws InvalidClassException + * @throws \SP\Core\Exceptions\InvalidClassException */ protected function initialize(): void { diff --git a/app/modules/api/Controllers/ClientController.php b/app/modules/api/Controllers/ClientController.php index 0453ac31..9852902a 100644 --- a/app/modules/api/Controllers/ClientController.php +++ b/app/modules/api/Controllers/ClientController.php @@ -66,7 +66,10 @@ final class ClientController extends ControllerBase $clientData = $this->clientService->getById($id); - $this->eventDispatcher->notifyEvent('show.client', new Event($this)); + $this->eventDispatcher->notifyEvent( + 'show.client', + new Event($this) + ); $this->eventDispatcher->notifyEvent( 'show.client', @@ -85,7 +88,10 @@ final class ClientController extends ControllerBase $out = $this->fractal ->createData( - new Item($clientData, new ClientAdapter($this->configData))); + new Item( + $clientData, + new ClientAdapter($this->configData) + )); if ($customFields) { $this->apiService->requireMasterPass(); @@ -117,6 +123,8 @@ final class ClientController extends ControllerBase $id = $this->clientService->create($clientData); + $clientData->setId($id); + $this->eventDispatcher->notifyEvent( 'create.client', new Event( @@ -234,7 +242,10 @@ final class ClientController extends ControllerBase $itemSearchData->setSeachString($this->apiService->getParamString('text')); $itemSearchData->setLimitCount($this->apiService->getParamInt('count', false, self::SEARCH_COUNT_ITEMS)); - $this->eventDispatcher->notifyEvent('search.client', new Event($this)); + $this->eventDispatcher->notifyEvent( + 'search.client', + new Event($this) + ); $this->returnResponse( ApiResponse::makeSuccess( @@ -249,9 +260,7 @@ final class ClientController extends ControllerBase } /** - * @throws DependencyException - * @throws NotFoundException - * @throws InvalidClassException + * @throws \SP\Core\Exceptions\InvalidClassException */ protected function initialize(): void { diff --git a/app/modules/api/Controllers/ConfigController.php b/app/modules/api/Controllers/ConfigController.php index 78da2558..8f5039c4 100644 --- a/app/modules/api/Controllers/ConfigController.php +++ b/app/modules/api/Controllers/ConfigController.php @@ -51,8 +51,8 @@ final class ConfigController extends ControllerBase $path = $this->apiService->getParamString('path', false, BACKUP_PATH); - $this->dic->get(FileBackupService::class) - ->doBackup($path); + $backupService = $this->dic->get(FileBackupService::class); + $backupService->doBackup($path); $this->eventDispatcher->notifyEvent( 'run.backup.end', @@ -64,9 +64,24 @@ final class ConfigController extends ControllerBase ) ); + $backupFiles = [ + 'files' => [ + 'app' => FileBackupService::getAppBackupFilename( + $path, + $backupService->getHash(), + true + ), + 'db' => FileBackupService::getDbBackupFilename( + $path, + $backupService->getHash(), + true + ) + ] + ]; + $this->returnResponse( ApiResponse::makeSuccess( - $path, + $backupFiles, null, __('Backup process finished') ) @@ -99,8 +114,9 @@ final class ConfigController extends ControllerBase ) ); - $this->dic->get(XmlExportService::class) - ->doExport($path, $password); + $exportService = $this->dic->get(XmlExportService::class); + $exportService->doExport($path, $password); + $this->eventDispatcher->notifyEvent( 'run.export.end', @@ -111,9 +127,15 @@ final class ConfigController extends ControllerBase ) ); + $exportFiles = [ + 'files' => [ + 'xml' => $exportService->getExportFile() + ] + ]; + $this->returnResponse( ApiResponse::makeSuccess( - $path, + $exportFiles, null, __('Export process finished') ) diff --git a/app/modules/api/Controllers/ControllerBase.php b/app/modules/api/Controllers/ControllerBase.php index ac030574..d48760fb 100644 --- a/app/modules/api/Controllers/ControllerBase.php +++ b/app/modules/api/Controllers/ControllerBase.php @@ -28,10 +28,9 @@ use Exception; use Klein\Klein; use League\Fractal\Manager; use Psr\Container\ContainerInterface; -use SP\Config\ConfigData; use SP\Config\ConfigDataInterface; use SP\Core\Acl\Acl; -use SP\Core\Context\StatelessContext; +use SP\Core\Context\ContextInterface; use SP\Core\Events\EventDispatcher; use SP\Core\Exceptions\SPException; use SP\Http\Json; @@ -48,10 +47,11 @@ use SP\Services\ServiceException; abstract class ControllerBase { protected const SEARCH_COUNT_ITEMS = 25; + protected ContainerInterface $dic; protected string $controllerName; protected string $actionName; - protected StatelessContext $context; + protected ContextInterface $context; protected EventDispatcher $eventDispatcher; protected ApiService $apiService; protected Klein $router; @@ -66,8 +66,8 @@ abstract class ControllerBase ) { $this->dic = $container; - $this->context = $container->get(StatelessContext::class); - $this->configData = $container->get(ConfigData::class); + $this->context = $container->get(ContextInterface::class); + $this->configData = $container->get(ConfigDataInterface::class); $this->eventDispatcher = $container->get(EventDispatcher::class); $this->router = $container->get(Klein::class); $this->apiService = $container->get(ApiService::class); @@ -133,8 +133,8 @@ abstract class ControllerBase */ private function sendJsonResponse(string $response): void { - $json = Json::factory($this->router->response()); - $json->returnRawJson($response); + Json::factory($this->router->response()) + ->returnRawJson($response); } final protected function returnResponseException(Exception $e): void diff --git a/app/modules/api/Controllers/TagController.php b/app/modules/api/Controllers/TagController.php index 377328b4..11e9ac0e 100644 --- a/app/modules/api/Controllers/TagController.php +++ b/app/modules/api/Controllers/TagController.php @@ -87,6 +87,8 @@ final class TagController extends ControllerBase $id = $this->tagService->create($tagData); + $tagData->setId($id); + $this->eventDispatcher->notifyEvent( 'create.tag', new Event( diff --git a/app/modules/api/Controllers/UserGroupController.php b/app/modules/api/Controllers/UserGroupController.php index 4b43f029..c81b07e3 100644 --- a/app/modules/api/Controllers/UserGroupController.php +++ b/app/modules/api/Controllers/UserGroupController.php @@ -93,6 +93,8 @@ final class UserGroupController extends ControllerBase $id = $this->userGroupService->create($userGroupData); + $userGroupData->setId($id); + $this->eventDispatcher->notifyEvent( 'create.userGroup', new Event($this, EventMessage::factory() diff --git a/app/modules/api/Init.php b/app/modules/api/Init.php index 9c74d258..2ef66337 100644 --- a/app/modules/api/Init.php +++ b/app/modules/api/Init.php @@ -25,10 +25,7 @@ namespace SP\Modules\Api; use Defuse\Crypto\Exception\EnvironmentIsBrokenException; -use DI\DependencyException; -use DI\NotFoundException; use Psr\Container\ContainerInterface; -use SP\Core\Context\ContextException; use SP\Core\Context\StatelessContext; use SP\Core\Exceptions\InitializationException; use SP\Core\Language; @@ -130,6 +127,10 @@ final class Init extends ModuleBase */ private function checkUpgrade(): void { + if (IS_TESTING) { + return; + } + UpgradeUtil::fixAppUpgrade($this->configData, $this->config); if ($this->configData->getUpgradeKey() diff --git a/app/modules/cli/definitions.php b/app/modules/cli/definitions.php deleted file mode 100644 index ed2fb7ca..00000000 --- a/app/modules/cli/definitions.php +++ /dev/null @@ -1,54 +0,0 @@ -. - */ - -use Monolog\Handler\StreamHandler; -use Monolog\Logger; -use Psr\Container\ContainerInterface; -use Psr\Log\LoggerInterface; -use SP\Modules\Cli\Commands\BackupCommand; -use SP\Modules\Cli\Commands\Crypt\UpdateMasterPasswordCommand; -use SP\Modules\Cli\Commands\InstallCommand; -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\ConsoleOutput; -use Symfony\Component\Console\Output\OutputInterface; -use function DI\autowire; -use function DI\create; - -return [ - LoggerInterface::class => static function (ContainerInterface $c) { - $logger = $c->get(Logger::class); - $logger->pushHandler(new StreamHandler(LOG_FILE)); - - return $logger; - }, - Application::class => create(Application::class), - OutputInterface::class => create(ConsoleOutput::class) - ->constructor(OutputInterface::VERBOSITY_NORMAL, true), - InputInterface::class => create(ArgvInput::class), - InstallCommand::class => autowire(), - BackupCommand::class => autowire(), - UpdateMasterPasswordCommand::class => autowire() -]; diff --git a/app/modules/cli/module.php b/app/modules/cli/module.php index 62202b4f..b152a9cd 100644 --- a/app/modules/cli/module.php +++ b/app/modules/cli/module.php @@ -22,5 +22,23 @@ * along with sysPass. If not, see . */ +use Monolog\Handler\StreamHandler; +use Monolog\Logger; +use Psr\Log\LoggerInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\OutputInterface; +use function DI\create; + const MODULE_PATH = __DIR__; const PLUGINS_PATH = MODULE_PATH . DIRECTORY_SEPARATOR . 'plugins'; + +return [ + LoggerInterface::class => static fn(Logger $logger) => $logger->pushHandler(new StreamHandler(LOG_FILE)), + Application::class => create(Application::class), + OutputInterface::class => create(ConsoleOutput::class) + ->constructor(OutputInterface::VERBOSITY_NORMAL, true), + InputInterface::class => create(ArgvInput::class) +]; diff --git a/composer.json b/composer.json index fb79ea49..af940081 100644 --- a/composer.json +++ b/composer.json @@ -29,8 +29,8 @@ "doctrine/common": "^v2.7", "guzzlehttp/guzzle": "^6.3", "monolog/monolog": "^1.23", - "symfony/debug" : "^v3.4", - "vlucas/phpdotenv" : "^v4.1", + "symfony/debug": "^v3.4", + "vlucas/phpdotenv": "^v4.1", "ext-pdo": "*", "ext-dom": "*", "ext-gd": "*", diff --git a/composer.lock b/composer.lock index d9d39db7..f6ea57dd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e123614aaac6578d9025c736e63dcebb", + "content-hash": "76501720109bd95fc02587f2d4dac0ce", "packages": [ { "name": "ademarre/binary-to-text-php", @@ -991,16 +991,16 @@ }, { "name": "guzzlehttp/promises", - "version": "1.4.1", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" + "reference": "136a635e2b4a49b9d79e9c8fee267ffb257fdba0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "url": "https://api.github.com/repos/guzzle/promises/zipball/136a635e2b4a49b9d79e9c8fee267ffb257fdba0", + "reference": "136a635e2b4a49b9d79e9c8fee267ffb257fdba0", "shasum": "" }, "require": { @@ -1012,7 +1012,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { @@ -1028,10 +1028,25 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle promises library", @@ -1040,22 +1055,36 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.1" + "source": "https://github.com/guzzle/promises/tree/1.5.0" }, - "time": "2021-03-07T09:25:29+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2021-10-07T13:05:22+00:00" }, { "name": "guzzlehttp/psr7", - "version": "1.8.2", + "version": "1.8.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91" + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", "shasum": "" }, "require": { @@ -1092,13 +1121,34 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, { "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" } ], @@ -1115,9 +1165,23 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.2" + "source": "https://github.com/guzzle/psr7/tree/1.8.3" }, - "time": "2021-04-26T09:17:50+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2021-10-05T13:56:00+00:00" }, { "name": "klein/klein", @@ -2127,12 +2191,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "4911abe63afbbba425931f44a3c62fc002973935" + "reference": "0488e161600117fc3a0d72397dad154729002f54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/4911abe63afbbba425931f44a3c62fc002973935", - "reference": "4911abe63afbbba425931f44a3c62fc002973935", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/0488e161600117fc3a0d72397dad154729002f54", + "reference": "0488e161600117fc3a0d72397dad154729002f54", "shasum": "" }, "conflict": { @@ -2205,7 +2269,7 @@ "ezsystems/ezplatform-admin-ui": ">=1.3,<1.3.5|>=1.4,<1.4.6", "ezsystems/ezplatform-admin-ui-assets": ">=4,<4.2.1|>=5,<5.0.1|>=5.1,<5.1.1", "ezsystems/ezplatform-kernel": "<=1.2.5|>=1.3,<=1.3.1", - "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<=1.3.1", + "ezsystems/ezplatform-rest": ">=1.2,<=1.2.2|>=1.3,<1.3.8", "ezsystems/ezplatform-user": ">=1,<1.0.1", "ezsystems/ezpublish-kernel": "<=6.13.8.1|>=7,<=7.5.15.1", "ezsystems/ezpublish-legacy": "<=2017.12.7.3|>=2018.6,<=2019.3.5.1", @@ -2230,13 +2294,14 @@ "friendsoftypo3/mediace": ">=7.6.2,<7.6.5", "froala/wysiwyg-editor": "<3.2.7", "fuel/core": "<1.8.1", - "getgrav/grav": "<=1.7.10", + "getgrav/grav": "<1.7.21", "getkirby/cms": "<=3.5.6", "getkirby/panel": "<2.5.14", + "gilacms/gila": "<=1.11.4", "gos/web-socket-bundle": "<1.10.4|>=2,<2.6.1|>=3,<3.3", "gree/jose": "<=2.2", "gregwar/rst": "<1.0.3", - "grumpydictator/firefly-iii": "<5.6", + "grumpydictator/firefly-iii": "<5.6.1", "guzzlehttp/guzzle": ">=4-rc.2,<4.2.4|>=5,<5.3.1|>=6,<6.2.1", "helloxz/imgurl": "<=2.31", "ibexa/post-install": "<=1.0.4", @@ -2263,6 +2328,7 @@ "laravel/framework": "<6.20.26|>=7,<8.40", "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10", "lavalite/cms": "<=5.8", + "lcobucci/jwt": ">=3.4,<3.4.6|>=4,<4.0.4|>=4.1,<4.1.5", "league/commonmark": "<0.18.3", "league/flysystem": "<1.1.4|>=2,<2.1.1", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", @@ -2426,6 +2492,7 @@ "theonedemon/phpwhois": "<=4.2.5", "titon/framework": ">=0,<9.9.99", "topthink/think": "<=6.0.9", + "topthink/thinkphp": "<=3.2.3", "tribalsystems/zenario": "<8.8.53370", "truckersmp/phpwhois": "<=4.3.1", "twig/twig": "<1.38|>=2,<2.7", @@ -2444,6 +2511,7 @@ "verot/class.upload.php": "<=1.0.3|>=2,<=2.0.4", "vrana/adminer": "<4.7.9", "wallabag/tcpdf": "<6.2.22", + "web-auth/webauthn-framework": ">=3.3,<3.3.4", "webcoast/deferred-image-processing": "<1.0.2", "wikimedia/parsoid": "<0.12.2", "willdurand/js-translation-bundle": "<2.1.1", @@ -2520,7 +2588,7 @@ "type": "tidelift" } ], - "time": "2021-09-20T21:03:12+00:00" + "time": "2021-09-30T18:03:50+00:00" }, { "name": "symfony/console", @@ -3649,16 +3717,16 @@ }, { "name": "vlucas/phpdotenv", - "version": "v4.2.0", + "version": "v4.2.1", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "da64796370fc4eb03cc277088f6fede9fde88482" + "reference": "d38f4d1edcbe32515a0ad593cbd4c858e337263c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/da64796370fc4eb03cc277088f6fede9fde88482", - "reference": "da64796370fc4eb03cc277088f6fede9fde88482", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/d38f4d1edcbe32515a0ad593cbd4c858e337263c", + "reference": "d38f4d1edcbe32515a0ad593cbd4c858e337263c", "shasum": "" }, "require": { @@ -3670,7 +3738,7 @@ "bamarni/composer-bin-plugin": "^1.4.1", "ext-filter": "*", "ext-pcre": "*", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20" + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.21" }, "suggest": { "ext-filter": "Required to use the boolean validator.", @@ -3679,7 +3747,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.1-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -3694,13 +3762,11 @@ "authors": [ { "name": "Graham Campbell", - "email": "graham@alt-three.com", - "homepage": "https://gjcampbell.co.uk/" + "email": "hello@gjcampbell.co.uk" }, { "name": "Vance Lucas", - "email": "vance@vancelucas.com", - "homepage": "https://vancelucas.com/" + "email": "vance@vancelucas.com" } ], "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", @@ -3711,7 +3777,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v4.2.0" + "source": "https://github.com/vlucas/phpdotenv/tree/v4.2.1" }, "funding": [ { @@ -3723,10 +3789,63 @@ "type": "tidelift" } ], - "time": "2021-01-20T15:11:48+00:00" + "time": "2021-10-02T19:17:08+00:00" } ], "packages-dev": [ + { + "name": "dg/bypass-finals", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/dg/bypass-finals.git", + "reference": "495f5bc762e7bf30a13ed8253f44bb3a701767bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dg/bypass-finals/zipball/495f5bc762e7bf30a13ed8253f44bb3a701767bb", + "reference": "495f5bc762e7bf30a13ed8253f44bb3a701767bb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "nette/tester": "^2.3", + "phpstan/phpstan": "^0.12" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + } + ], + "description": "Removes final keyword from source code on-the-fly and allows mocking of final methods and classes", + "keywords": [ + "finals", + "mocking", + "phpunit", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/dg/bypass-finals/issues", + "source": "https://github.com/dg/bypass-finals/tree/v1.3.1" + }, + "time": "2021-04-09T10:42:55+00:00" + }, { "name": "doctrine/instantiator", "version": "1.4.0", @@ -4246,16 +4365,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f" + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", "shasum": "" }, "require": { @@ -4290,9 +4409,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" }, - "time": "2021-09-17T15:28:14+00:00" + "time": "2021-10-02T14:08:47+00:00" }, { "name": "phpspec/prophecy", diff --git a/lib/Base.php b/lib/Base.php index 1f18107b..69b9fad2 100644 --- a/lib/Base.php +++ b/lib/Base.php @@ -45,19 +45,20 @@ $memInit = memory_get_usage(); require __DIR__ . DS . 'BaseFunctions.php'; require VENDOR_PATH . DS . 'autoload.php'; -require __DIR__ . DS . 'SplClassLoader.php'; $dotenv = Dotenv::createImmutable(APP_ROOT); $dotenv->load(); defined('APP_MODULE') || define('APP_MODULE', 'web'); -define('DEBUG', getenv('DEBUG') || false); +define('DEBUG', getenv('DEBUG')); +define('IS_TESTING', + getenv('IS_TESTING') + ?: defined('TEST_ROOT')); define('CONFIG_PATH', getenv('CONFIG_PATH') ?: APP_PATH . DS . 'config'); - // Setup config files const OLD_CONFIG_FILE = CONFIG_PATH . DS . 'config.php'; @@ -73,6 +74,7 @@ define('MIMETYPES_FILE', define('LOG_FILE', getenv('LOG_FILE') ?: CONFIG_PATH . DS . 'syspass.log'); + const LOCK_FILE = CONFIG_PATH . DS . '.lock'; // Setup application paths @@ -87,13 +89,17 @@ define('TMP_PATH', ?: APP_PATH . DS . 'temp'); try { - $builder = new ContainerBuilder(); - $builder->writeProxiesToFile(true, CACHE_PATH . DS . 'proxies'); - $builder->addDefinitions(BASE_PATH . DS . 'Definitions.php'); + $moduleDefinitions = initModule(APP_MODULE); - initModule(APP_MODULE, $builder); + $dic = (new ContainerBuilder) + ->writeProxiesToFile(true, CACHE_PATH . DS . 'proxies') + ->addDefinitions( + BASE_PATH . DS . 'Definitions.php', + $moduleDefinitions + ) + ->build(); - Bootstrap::run($builder->build()); + Bootstrap::run($dic); } catch (Exception $e) { processException($e); diff --git a/lib/BaseFunctions.php b/lib/BaseFunctions.php index e599b63b..9e63a72c 100644 --- a/lib/BaseFunctions.php +++ b/lib/BaseFunctions.php @@ -22,12 +22,11 @@ * along with sysPass. If not, see . */ +use SP\Core\Exceptions\SPException; + /** * [type] [caller] data */ - -use DI\ContainerBuilder; - const LOG_FORMAT = "[%s] [%s] %s"; /** * [timestamp] [type] [caller] data @@ -54,47 +53,27 @@ function logger($data, ?string $type = 'DEBUG') $date = date('Y-m-d H:i:s'); $caller = getLastCaller(); - - if (is_scalar($data)) { - $line = sprintf( - LOG_FORMAT_OWN, - $date, - $type, - $data, - $caller - ); - } else { - $line = sprintf( - LOG_FORMAT_OWN, - $date, - $type, - print_r($data, true), - $caller - ); - } + $line = sprintf( + LOG_FORMAT_OWN, + $date, + $type, + is_scalar($data) ? $data : print_r($data, true), + $caller + ); $useOwn = (!defined('LOG_FILE') - || !error_log($line, 3, LOG_FILE) + || !@error_log($line, 3, LOG_FILE) ); if ($useOwn === false) { - if (is_scalar($data)) { - $line = sprintf( - LOG_FORMAT, - $type, - $data, - $caller - ); - } else { - $line = sprintf( - LOG_FORMAT, - $type, - print_r($data, true), - $caller - ); - } + $line = sprintf( + LOG_FORMAT, + $type, + is_scalar($data) ? $data : print_r($data, true), + $caller + ); - error_log($line); + @error_log($line); } } @@ -176,7 +155,7 @@ function formatStackTrace(Throwable $e): string * * @param \Exception $exception */ -function processException(\Exception $exception) +function processException(Exception $exception) { logger(sprintf( "%s\n%s", @@ -278,29 +257,28 @@ function getElapsedTime(float $from): float /** * Inicializar módulo + * + * @throws \SP\Core\Exceptions\SPException */ -function initModule(string $module, ?ContainerBuilder $builder = null) +function initModule(string $module): array { - $dir = dir(MODULES_PATH); + logger(sprintf('Initializing module: %s', $module)); - while (false !== ($entry = $dir->read())) { - if ($entry === $module) { - $moduleFile = MODULES_PATH . DIRECTORY_SEPARATOR . $entry . DIRECTORY_SEPARATOR . 'module.php'; - $definitionsFile = MODULES_PATH . DIRECTORY_SEPARATOR . $entry . DIRECTORY_SEPARATOR . 'definitions.php'; + $moduleFile = MODULES_PATH . DIRECTORY_SEPARATOR . $module . DIRECTORY_SEPARATOR . 'module.php'; - if (file_exists($moduleFile)) { - require $moduleFile; - } + if (is_dir(MODULES_PATH) && file_exists($moduleFile)) { + $definitions = require $moduleFile; - if ($builder !== null && file_exists($definitionsFile)) { - $definitions = require $definitionsFile; - - $builder->addDefinitions($definitions); - } + if (is_array($definitions)) { + return $definitions; } + } else { + throw new SPException('Either module dir or module file don\'t exist'); } - $dir->close(); + logger(sprintf('No definitions found for module: %s', $module)); + + return []; } /** diff --git a/lib/Definitions.php b/lib/Definitions.php index 81f98ed0..a6610291 100644 --- a/lib/Definitions.php +++ b/lib/Definitions.php @@ -29,9 +29,8 @@ use SP\Config\Config; use SP\Config\ConfigDataInterface; use SP\Core\Acl\Acl; use SP\Core\Acl\Actions; +use SP\Core\Context\ContextFactory; use SP\Core\Context\ContextInterface; -use SP\Core\Context\SessionContext; -use SP\Core\Context\StatelessContext; use SP\Core\Crypt\CSRF; use SP\Core\MimeTypes; use SP\Core\UI\Theme; @@ -54,45 +53,41 @@ return [ Request::class => create(Request::class) ->constructor(\Klein\Request::createFromGlobals()), ContextInterface::class => - static function (ContainerInterface $c) { - if (APP_MODULE === 'web') { - return $c->get(SessionContext::class); - } - - return $c->get(StatelessContext::class); - }, + static fn() => ContextFactory::getForModule(APP_MODULE), Config::class => - static function (ContainerInterface $c) { - return new Config( - new XmlHandler(new FileHandler(CONFIG_FILE)), - new FileCache(Config::CONFIG_CACHE_FILE), - $c); - }, + static fn(ContainerInterface $c) => new Config( + new XmlHandler(new FileHandler(CONFIG_FILE)), + new FileCache(Config::CONFIG_CACHE_FILE), + $c->get(ContextInterface::class), + $c + ), ConfigDataInterface::class => - static function (Config $config) { - return $config->getConfigData(); - }, + static fn(Config $config) => $config->getConfigData(), DBStorageInterface::class => create(MySQLHandler::class) - ->constructor(factory([DatabaseConnectionData::class, 'getFromConfig'])), + ->constructor( + factory([DatabaseConnectionData::class, 'getFromConfig']) + ), Actions::class => - static function (ContainerInterface $c) { - return new Actions( - new FileCache(Actions::ACTIONS_CACHE_FILE), - new XmlHandler(new FileHandler(ACTIONS_FILE)) - ); - }, + static fn() => new Actions( + new FileCache(Actions::ACTIONS_CACHE_FILE), + new XmlHandler(new FileHandler(ACTIONS_FILE)) + ), MimeTypes::class => - static function () { - return new MimeTypes( - new FileCache(MimeTypes::MIME_CACHE_FILE), - new XmlHandler(new FileHandler(MIMETYPES_FILE)) - ); - }, + static fn() => new MimeTypes( + new FileCache(MimeTypes::MIME_CACHE_FILE), + new XmlHandler(new FileHandler(MIMETYPES_FILE)) + ), Acl::class => autowire(Acl::class) - ->constructorParameter('action', get(Actions::class)), + ->constructorParameter( + 'action', + get(Actions::class) + ), ThemeInterface::class => autowire(Theme::class) ->constructorParameter('module', APP_MODULE) - ->constructorParameter('fileCache', new FileCache(Theme::ICONS_CACHE_FILE)), + ->constructorParameter( + 'fileCache', + new FileCache(Theme::ICONS_CACHE_FILE) + ), PHPMailer::class => create(PHPMailer::class) ->constructor(true), Logger::class => create(Logger::class) diff --git a/lib/SP/Adapters/AccountAdapter.php b/lib/SP/Adapters/AccountAdapter.php index 08166e2d..a4e996d9 100644 --- a/lib/SP/Adapters/AccountAdapter.php +++ b/lib/SP/Adapters/AccountAdapter.php @@ -99,10 +99,12 @@ final class AccountAdapter extends AdapterBase 'links' => [ [ 'rel' => 'self', - 'uri' => Link::getDeepLink($account->getId(), + 'uri' => Link::getDeepLink( + $account->getId(), ActionsInterface::ACCOUNT_VIEW, $this->configData, - true) + true + ) ] ], ]; diff --git a/lib/SP/Adapters/CategoryAdapter.php b/lib/SP/Adapters/CategoryAdapter.php index 9710ade5..af0d702a 100644 --- a/lib/SP/Adapters/CategoryAdapter.php +++ b/lib/SP/Adapters/CategoryAdapter.php @@ -67,10 +67,12 @@ final class CategoryAdapter extends AdapterBase 'links' => [ [ 'rel' => 'self', - 'uri' => Link::getDeepLink($data->getId(), + 'uri' => Link::getDeepLink( + $data->getId(), ActionsInterface::CATEGORY_VIEW, $this->configData, - true) + true + ) ] ], ]; diff --git a/lib/SP/Adapters/ClientAdapter.php b/lib/SP/Adapters/ClientAdapter.php index bbe08401..62c30127 100644 --- a/lib/SP/Adapters/ClientAdapter.php +++ b/lib/SP/Adapters/ClientAdapter.php @@ -63,7 +63,7 @@ final class ClientAdapter extends AdapterBase 'id' => $data->getId(), 'name' => $data->getName(), 'description' => $data->getDescription(), - 'global' => $data->isGlobal, + 'isGlobal' => $data->isGlobal, 'customFields' => null, 'links' => [ [ diff --git a/lib/SP/Bootstrap.php b/lib/SP/Bootstrap.php index 3f6e5e4f..2b19797f 100644 --- a/lib/SP/Bootstrap.php +++ b/lib/SP/Bootstrap.php @@ -33,10 +33,8 @@ use Klein\Response; use PHPMailer\PHPMailer\Exception; use Psr\Container\ContainerInterface; use RuntimeException; -use SP\Config\Config; use SP\Config\ConfigDataInterface; use SP\Config\ConfigUtil; -use SP\Core\Exceptions\ConfigException; use SP\Core\Exceptions\InitializationException; use SP\Core\Exceptions\SessionTimeout; use SP\Core\Language; @@ -93,18 +91,15 @@ final class Bootstrap /** * Bootstrap constructor. - * - * @throws \DI\DependencyException - * @throws \DI\NotFoundException */ - private function __construct(Container $container) + public function __construct(ContainerInterface $container) { self::$container = $container; // Set the default language Language::setLocales('en_US'); - $this->configData = $container->get(Config::class)->getConfigData(); + $this->configData = $container->get(ConfigDataInterface::class); $this->router = $container->get(Klein::class); $this->request = $container->get(Request::class); @@ -209,14 +204,14 @@ final class Bootstrap logger('Routing call: ' . $controllerClass . '::' . $method); - return call_user_func([new $controllerClass(self::$container, $method, $apiRequest), $method]); + return call_user_func([new $controllerClass(self::$container, $method), $method]); } catch (\Exception $e) { processException($e); /** @var Response $response */ $response->headers()->set('Content-type', 'application/json; charset=utf-8'); - return $response->body(JsonRpcResponse::getResponseException($e, 0)); + return $response->body(JsonRpcResponse::getResponseException($e, 0)); } finally { $this->router->skipRemaining(); } @@ -233,13 +228,11 @@ final class Bootstrap } /** - * @throws ConfigException - * @throws Core\Exceptions\CheckException - * @throws InitializationException - * @throws Services\Upgrade\UpgradeException - * @throws DependencyException - * @throws NotFoundException - * @throws Storage\File\FileException + * @throws \SP\Core\Exceptions\CheckException + * @throws \SP\Core\Exceptions\ConfigException + * @throws \SP\Core\Exceptions\InitializationException + * @throws \SP\Services\Upgrade\UpgradeException + * @throws \SP\Storage\File\FileException */ protected function initializeCommon(): void { @@ -323,7 +316,10 @@ final class Bootstrap Debug::enable(); } else { error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE)); - ini_set('display_errors', 0); + + if (!headers_sent()) { + ini_set('display_errors', 0); + } } if (!file_exists(LOG_FILE) @@ -337,9 +333,11 @@ final class Bootstrap date_default_timezone_set('UTC'); } - // Avoid PHP session cookies from JavaScript - ini_set('session.cookie_httponly', '1'); - ini_set('session.save_handler', 'files'); + if (!headers_sent()) { + // Avoid PHP session cookies from JavaScript + ini_set('session.cookie_httponly', '1'); + ini_set('session.save_handler', 'files'); + } } /** @@ -365,11 +363,8 @@ final class Bootstrap /** * Cargar la configuración * - * @throws ConfigException - * @throws Services\Upgrade\UpgradeException - * @throws Storage\File\FileException - * @throws DependencyException - * @throws NotFoundException + * @throws \SP\Core\Exceptions\ConfigException + * @throws \SP\Services\Upgrade\UpgradeException */ private function initConfig(): void { @@ -381,14 +376,17 @@ final class Bootstrap /** * Comprobar la versión de configuración y actualizarla * - * @throws Services\Upgrade\UpgradeException - * @throws Storage\File\FileException - * @throws DependencyException - * @throws NotFoundException + * @throws \SP\Services\Upgrade\UpgradeException */ private function checkConfigVersion(): void { - if (file_exists(OLD_CONFIG_FILE)) { + // Do not check config version when testing + if (IS_TESTING) { + return; + } + + if (defined('OLD_CONFIG_FILE') + && file_exists(OLD_CONFIG_FILE)) { $upgradeConfigService = self::$container->get(UpgradeConfigService::class); $upgradeConfigService->upgradeOldConfigFile(VersionUtil::getVersionStringNormalized()); } @@ -483,11 +481,7 @@ final class Bootstrap protected function initializePluginClasses(): void { - $loader = require APP_ROOT . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; - - foreach (PluginManager::getPlugins() as $base) { - $loader->addPsr4($base['namespace'], $base['dir']); - } + PluginManager::getPlugins(); } public static function getContainer(): ContainerInterface @@ -532,4 +526,9 @@ final class Bootstrap throw new InitializationException('Unknown module'); } } + + public function getRouter(): Klein + { + return $this->router; + } } \ No newline at end of file diff --git a/lib/SP/Config/Config.php b/lib/SP/Config/Config.php index 293f53a4..d5833639 100644 --- a/lib/SP/Config/Config.php +++ b/lib/SP/Config/Config.php @@ -57,19 +57,18 @@ final class Config private ContainerInterface $dic; /** - * Config constructor. - * * @throws ConfigException */ public function __construct( XmlFileStorageInterface $fileStorage, FileCacheInterface $fileCache, + ContextInterface $context, ContainerInterface $dic ) { $this->fileCache = $fileCache; $this->fileStorage = $fileStorage; - $this->context = $dic->get(ContextInterface::class); + $this->context = $context; $this->dic = $dic; $this->initialize(); @@ -176,8 +175,7 @@ final class Config ): Config { if ($backup) { - $this->dic->get(ConfigBackupService::class) - ->backup($configData); + $this->dic->get(ConfigBackupService::class)->backup($configData); } $configSaver = $this->context->getUserData()->getLogin() diff --git a/lib/SP/Core/Context/ContextBase.php b/lib/SP/Core/Context/ContextBase.php index e7419cba..b61f9729 100644 --- a/lib/SP/Core/Context/ContextBase.php +++ b/lib/SP/Core/Context/ContextBase.php @@ -79,6 +79,11 @@ abstract class ContextBase implements ContextInterface : $this->trasient->get($key, $default); } + public function isInitialized(): bool + { + return $this->context !== null; + } + /** * @throws ContextException */ @@ -123,7 +128,9 @@ abstract class ContextBase implements ContextInterface { $this->checkContext(); - return is_numeric($default) ? (int)$this->context->get($key, $default) : $this->context->get($key, $default); + return is_numeric($default) + ? (int)$this->context->get($key, $default) + : $this->context->get($key, $default); } /** diff --git a/lib/SP/Core/Context/ContextFactory.php b/lib/SP/Core/Context/ContextFactory.php new file mode 100644 index 00000000..224b3467 --- /dev/null +++ b/lib/SP/Core/Context/ContextFactory.php @@ -0,0 +1,40 @@ +. + */ + +namespace SP\Core\Context; + +/** + * + */ +final class ContextFactory +{ + public static function getForModule(string $module): ContextInterface + { + if ($module === 'web') { + return new SessionContext(); + } + + return new StatelessContext(); + } +} \ No newline at end of file diff --git a/lib/SP/Core/Context/ContextInterface.php b/lib/SP/Core/Context/ContextInterface.php index b3c2c3f5..8b7921a4 100644 --- a/lib/SP/Core/Context/ContextInterface.php +++ b/lib/SP/Core/Context/ContextInterface.php @@ -40,87 +40,65 @@ interface ContextInterface */ public function initialize(); + public function isInitialized(): bool; + /** * Establecer la hora de carga de la configuración - * - * @param int $time */ public function setConfigTime(int $time); /** * Devolver la hora de carga de la configuración - * - * @return int */ public function getConfigTime(): int; /** * Establece los datos del usuario en la sesión. - * - * @param UserLoginResponse|null $userLoginResponse */ public function setUserData(?UserLoginResponse $userLoginResponse = null); /** * Obtiene el objeto de perfil de usuario de la sesión. - * - * @return ProfileData|null */ public function getUserProfile(): ?ProfileData; /** * Establece el objeto de perfil de usuario en la sesión. - * - * @param ProfileData $ProfileData */ - public function setUserProfile(ProfileData $ProfileData); + public function setUserProfile(ProfileData $profileData); /** * Returns if user is logged in - * - * @return bool */ public function isLoggedIn(): bool; /** * Devuelve los datos del usuario en la sesión. - * - * @return UserLoginResponse */ public function getUserData(): UserLoginResponse; /** * Establecer el lenguaje de la sesión - * - * @param $locale */ - public function setLocale($locale); + public function setLocale(string $locale); /** * Devuelve el lenguaje de la sesión - * - * @return string|null */ public function getLocale(): ?string; /** * Devuelve el estado de la aplicación - * - * @return bool|null */ public function getAppStatus(): ?bool; /** * Establecer el estado de la aplicación - * - * @param string $status */ public function setAppStatus(string $status); /** * Reset del estado de la aplicación - * - * @return bool|null */ public function resetAppStatus(): ?bool; @@ -136,7 +114,6 @@ interface ContextInterface * @param string $key * @param mixed $value * - * @return mixed * @throws ContextException */ public function setTrasientKey(string $key, $value); @@ -154,8 +131,6 @@ interface ContextInterface /** * Sets a temporary master password - * - * @param string $password */ public function setTemporaryMasterPass(string $password); @@ -163,16 +138,8 @@ interface ContextInterface * @param string $pluginName * @param string $key * @param mixed $value - * - * @return mixed */ public function setPluginKey(string $pluginName, string $key, $value); - /** - * @param string $pluginName - * @param string $key - * - * @return mixed - */ public function getPluginKey(string $pluginName, string $key); } \ No newline at end of file diff --git a/lib/SP/Core/Context/SessionContext.php b/lib/SP/Core/Context/SessionContext.php index afb2b1d2..f004bdc7 100644 --- a/lib/SP/Core/Context/SessionContext.php +++ b/lib/SP/Core/Context/SessionContext.php @@ -346,10 +346,8 @@ final class SessionContext extends ContextBase /** * Establecer el lenguaje de la sesión - * - * @param $locale */ - public function setLocale($locale): void + public function setLocale(string $locale): void { $this->setContextKey('locale', $locale); } @@ -480,7 +478,11 @@ final class SessionContext extends ContextBase public function initialize(): void { // Si la sesión no puede ser iniciada, devolver un error 500 - if (session_start() === false) { + if (headers_sent($filename, $line) + || @session_start() === false) { + + logger(sprintf('Headers sent in %s:%d file', $filename, $line)); + throw new ContextException(__u('Session cannot be initialized')); } diff --git a/lib/SP/Core/Context/StatelessContext.php b/lib/SP/Core/Context/StatelessContext.php index 42496cbc..03c21567 100644 --- a/lib/SP/Core/Context/StatelessContext.php +++ b/lib/SP/Core/Context/StatelessContext.php @@ -97,11 +97,11 @@ final class StatelessContext extends ContextBase /** * Establece el objeto de perfil de usuario en la sesión. * - * @param ProfileData $ProfileData + * @param ProfileData $profileData */ - public function setUserProfile(ProfileData $ProfileData): void + public function setUserProfile(ProfileData $profileData): void { - $this->setContextKey('userProfile', $ProfileData); + $this->setContextKey('userProfile', $profileData); } /** @@ -126,10 +126,8 @@ final class StatelessContext extends ContextBase /** * Establecer el lenguaje de la sesión - * - * @param $locale */ - public function setLocale($locale): void + public function setLocale(string $locale): void { $this->setContextKey('locale', $locale); } diff --git a/lib/SP/Core/Crypt/Cookie.php b/lib/SP/Core/Crypt/Cookie.php index 041e1f10..c287699e 100644 --- a/lib/SP/Core/Crypt/Cookie.php +++ b/lib/SP/Core/Crypt/Cookie.php @@ -96,7 +96,7 @@ abstract class Cookie protected function setCookie(string $data): bool { // Do not try to set cookies when testing - if (APP_MODULE === 'tests') { + if (IS_TESTING) { return true; } diff --git a/lib/SP/Core/Language.php b/lib/SP/Core/Language.php index 37dae624..18a39364 100644 --- a/lib/SP/Core/Language.php +++ b/lib/SP/Core/Language.php @@ -184,6 +184,10 @@ final class Language */ public function setAppLocales(): void { + if (!$this->context->isInitialized()) { + return; + } + if ($this->configData->getSiteLang() !== $this->context->getLocale()) { self::setLocales($this->configData->getSiteLang()); diff --git a/lib/SP/DataModel/CategoryData.php b/lib/SP/DataModel/CategoryData.php index ce3252f7..a9e9c03c 100644 --- a/lib/SP/DataModel/CategoryData.php +++ b/lib/SP/DataModel/CategoryData.php @@ -33,92 +33,54 @@ defined('APP_ROOT') || die(); */ class CategoryData extends DataModelBase implements DataModelInterface { - /** - * @var int - */ - public $id = 0; - /** - * @var string - */ - public $name = ''; - /** - * @var string - */ - public $description = ''; - /** - * @var string - */ - public $hash = ''; + public ?int $id = null; + public ?string $name = null; + public ?string $description = null; + public ?string $hash = null; - /** - * accountDefaultPermissionData constructor. - * - * @param int $id - * @param string $name - * @param string $description - */ - public function __construct($id = null, $name = null, $description = null) + public function __construct( + ?int $id = null, + ?string $name = null, + ?string $description = null + ) { $this->id = $id; $this->name = $name; $this->description = $description; } - /** - * @return int - */ - public function getId() + public function getId(): int { return (int)$this->id; } - /** - * @param int $id - * - * @return $this - */ - public function setId($id) + public function setId(int $id): CategoryData { - $this->id = (int)$id; + $this->id = $id; return $this; } - /** - * @return string - */ - public function getName() + public function getName(): ?string { return $this->name; } - /** - * @param string $name - */ - public function setName($name) + public function setName(string $name): void { $this->name = $name; } - /** - * @return string - */ - public function getDescription() + public function getDescription(): ?string { return $this->description; } - /** - * @param string $description - */ - public function setDescription($description) + public function setDescription(?string $description): void { $this->description = $description; } - /** - * @return string - */ - public function getHash() + public function getHash(): ?string { return $this->hash; } diff --git a/lib/SP/DataModel/ClientData.php b/lib/SP/DataModel/ClientData.php index 50941f2a..868cfa39 100644 --- a/lib/SP/DataModel/ClientData.php +++ b/lib/SP/DataModel/ClientData.php @@ -33,110 +33,65 @@ defined('APP_ROOT') || die(); */ class ClientData extends DataModelBase implements DataModelInterface { - /** - * @var int - */ - public $id = 0; - /** - * @var string - */ - public $name = ''; - /** - * @var string - */ - public $description = ''; - /** - * @var string - */ - public $hash = ''; - /** - * @var int - */ - public $isGlobal = 0; + public ?int $id = null; + public ?string $name = null; + public ?string $description = null; + public ?string $hash = null; + public ?int $isGlobal = null; - /** - * CustomerData constructor. - * - * @param int $id - * @param string $name - * @param string $description - */ - public function __construct($id = null, $name = null, $description = null) + public function __construct( + ?int $id = null, + ?string $name = null, + ?string $description = null + ) { $this->id = $id; $this->name = $name; $this->description = $description; } - /** - * @return int - */ - public function getId() + public function getId(): ?int { - return (int)$this->id; + return $this->id; } - /** - * @param int $id - */ - public function setId($id) + public function setId(int $id): void { - $this->id = (int)$id; + $this->id = $id; } - /** - * @return string - */ - public function getName() + public function getName(): ?string { return $this->name; } - /** - * @param string $name - */ - public function setName($name) + public function setName(string $name): void { $this->name = $name; } - /** - * @return string - */ - public function getDescription() + public function getDescription(): ?string { return $this->description; } - /** - * @param string $description - */ - public function setDescription($description) + public function setDescription(?string $description): void { $this->description = $description; } - /** - * @return string - */ - public function getHash() + public function getHash(): ?string { return $this->hash; } - /** - * @return int - */ - public function getIsGlobal() + public function getIsGlobal(): ?int { - return (int)$this->isGlobal; + return $this->isGlobal; } - /** - * @param int $isGlobal - */ - public function setIsGlobal($isGlobal) + public function setIsGlobal(?int $isGlobal): void { - $this->isGlobal = (int)$isGlobal; + $this->isGlobal = $isGlobal; } } \ No newline at end of file diff --git a/lib/SP/DataModel/Dto/AccountDetailsResponse.php b/lib/SP/DataModel/Dto/AccountDetailsResponse.php index 6cb97fc9..eb1172df 100644 --- a/lib/SP/DataModel/Dto/AccountDetailsResponse.php +++ b/lib/SP/DataModel/Dto/AccountDetailsResponse.php @@ -28,32 +28,24 @@ use SP\DataModel\AccountVData; use SP\DataModel\ItemData; /** - * Class AccountDto - * - * @package SP\DataModel\Dto + * Class AccountDetailsResponse */ class AccountDetailsResponse { - /** - * @var int - */ - private $id; - /** - * @var AccountVData - */ - private $accountVData; + private int $id; + private AccountVData $accountVData; /** * @var ItemData[] Los usuarios secundarios de la cuenta. */ - private $users = []; + private array $users = []; /** * @var ItemData[] Los grupos secundarios de la cuenta. */ - private $userGroups = []; + private array $userGroups = []; /** * @var ItemData[] Las etiquetas de la cuenta. */ - private $tags = []; + private array $tags = []; /** * AccountDetailsResponse constructor. @@ -61,7 +53,7 @@ class AccountDetailsResponse * @param int $id * @param AccountVData $accountVData */ - public function __construct($id, AccountVData $accountVData) + public function __construct(int $id, AccountVData $accountVData) { $this->id = $id; $this->accountVData = $accountVData; @@ -70,7 +62,7 @@ class AccountDetailsResponse /** * @return ItemData[] */ - public function getUsers() + public function getUsers(): array { return $this->users; } @@ -78,7 +70,7 @@ class AccountDetailsResponse /** * @param ItemData[] $users */ - public function setUsers(array $users) + public function setUsers(array $users): void { $this->users = $users; } @@ -86,7 +78,7 @@ class AccountDetailsResponse /** * @return ItemData[] */ - public function getUserGroups() + public function getUserGroups(): array { return $this->userGroups; } @@ -94,7 +86,7 @@ class AccountDetailsResponse /** * @param ItemData[] $userGroups */ - public function setUserGroups(array $userGroups) + public function setUserGroups(array $userGroups): void { $this->userGroups = $userGroups; } @@ -102,7 +94,7 @@ class AccountDetailsResponse /** * @return ItemData[] */ - public function getTags() + public function getTags(): array { return $this->tags; } @@ -110,7 +102,7 @@ class AccountDetailsResponse /** * @param ItemData[] $tags */ - public function setTags(array $tags) + public function setTags(array $tags): void { $this->tags = $tags; } @@ -118,7 +110,7 @@ class AccountDetailsResponse /** * @return AccountVData */ - public function getAccountVData() + public function getAccountVData(): AccountVData { return $this->accountVData; } @@ -126,7 +118,7 @@ class AccountDetailsResponse /** * @return int */ - public function getId() + public function getId(): int { return $this->id; } diff --git a/lib/SP/DataModel/UserGroupData.php b/lib/SP/DataModel/UserGroupData.php index f3fa54ea..1c5d5df2 100644 --- a/lib/SP/DataModel/UserGroupData.php +++ b/lib/SP/DataModel/UserGroupData.php @@ -33,83 +33,47 @@ defined('APP_ROOT') || die(); */ class UserGroupData extends DataModelBase implements DataModelInterface { - /** - * @var int - */ - public $id = 0; - /** - * @var string - */ - public $name; - /** - * @var string - */ - public $description; - /** - * @var array - */ - public $users; + public int $id = 0; + public string $name; + public ?string $description = null; + public ?array $users = null; - /** - * @return int - */ - public function getId() + public function getId(): int { - return (int)$this->id; + return $this->id; } - /** - * @param int $id - */ - public function setId($id) + public function setId(int $id): void { - $this->id = (int)$id; + $this->id = $id; } - /** - * @return string - */ - public function getName() + public function getName(): string { return $this->name; } - /** - * @param string $name - */ - public function setName($name) + public function setName(string $name): void { $this->name = $name; } - /** - * @return string - */ - public function getDescription() + public function getDescription(): ?string { return $this->description; } - /** - * @param string $description - */ - public function setDescription($description) + public function setDescription(?string $description): void { $this->description = $description; } - /** - * @return array - */ - public function getUsers() + public function getUsers(): ?array { return $this->users; } - /** - * @param array $users - */ - public function setUsers($users) + public function setUsers(?array $users): void { $this->users = $users; } diff --git a/lib/SP/Http/Client.php b/lib/SP/Http/Client.php index effdf384..58537a8e 100644 --- a/lib/SP/Http/Client.php +++ b/lib/SP/Http/Client.php @@ -41,11 +41,18 @@ final class Client ]; if ($configData->isProxyEnabled()) { - $options['proxy'] = sprintf('tcp://%s:%d', $configData->getProxyServer(), $configData->getProxyPort()); + $options['proxy'] = sprintf( + 'tcp://%s:%d', + $configData->getProxyServer(), + $configData->getProxyPort() + ); if (!empty($configData->getProxyUser()) && !empty($configData->getProxyPass())) { - $options['auth'] = [$configData->getProxyUser(), $configData->getProxyPass()]; + $options['auth'] = [ + $configData->getProxyUser(), + $configData->getProxyPass() + ]; } } diff --git a/lib/SP/Http/Request.php b/lib/SP/Http/Request.php index 8a581e5c..c504e9f7 100644 --- a/lib/SP/Http/Request.php +++ b/lib/SP/Http/Request.php @@ -121,19 +121,18 @@ final class Request return $realPath; } - /** - * @return array|string - */ - public function getClientAddress(bool $fullForwarded = false) + public function getClientAddress(bool $fullForwarded = false): string { - if (APP_MODULE === 'tests') { + if (IS_TESTING) { return '127.0.0.1'; } $forwarded = $this->getForwardedFor(); if ($forwarded !== null) { - return $fullForwarded ? implode(',', $forwarded) : $forwarded[0]; + return $fullForwarded + ? implode(',', $forwarded) + : $forwarded[0]; } return $this->request->server()->get('REMOTE_ADDR', ''); diff --git a/lib/SP/Plugin/PluginManager.php b/lib/SP/Plugin/PluginManager.php index 15f1d882..198a8ded 100644 --- a/lib/SP/Plugin/PluginManager.php +++ b/lib/SP/Plugin/PluginManager.php @@ -84,32 +84,29 @@ final class PluginManager */ public static function getPlugins(): array { - if (is_dir(PLUGINS_PATH)) { - $dir = dir(PLUGINS_PATH); - $plugins = []; + $plugins = []; - if ($dir) { - while (false !== ($entry = $dir->read())) { - $pluginDir = PLUGINS_PATH . DIRECTORY_SEPARATOR . $entry; - $pluginFile = $pluginDir . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'Plugin.php'; + if (is_dir(PLUGINS_PATH) + && ($dir = dir(PLUGINS_PATH))) { - if (strpos($entry, '.') === false - && is_dir($pluginDir) - && file_exists($pluginFile) - ) { - logger(sprintf('Plugin found: %s', $pluginDir)); + while (false !== ($entry = $dir->read())) { + $pluginDir = PLUGINS_PATH . DS . $entry; + $pluginFile = $pluginDir . DS . 'src' . DS . 'lib' . DS . 'Plugin.php'; - $plugins[$entry] = require $pluginDir . DIRECTORY_SEPARATOR . 'base.php'; - } + if (strpos($entry, '.') === false + && is_dir($pluginDir) + && file_exists($pluginFile) + ) { + logger(sprintf('Plugin found: %s', $pluginDir)); + + $plugins[$entry] = require $pluginDir . DS . 'base.php'; } - - $dir->close(); } - return $plugins; + $dir->close(); } - return []; + return $plugins; } /** diff --git a/lib/SP/Providers/Log/LoggerBase.php b/lib/SP/Providers/Log/LoggerBase.php index d4fd5dc2..91564a46 100644 --- a/lib/SP/Providers/Log/LoggerBase.php +++ b/lib/SP/Providers/Log/LoggerBase.php @@ -74,27 +74,41 @@ abstract class LoggerBase extends Provider implements EventReceiver { $this->language->setAppLocales(); - $userLogin = $this->context->getUserData()->getLogin() ?: 'N/A'; + $userLogin = 'N/A'; + + if ($this->context->isInitialized()) { + $userLogin = $this->context->getUserData()->getLogin() ?? 'N/A'; + } + $source = $event->getSource(); if ($source instanceof Exception) { - $this->logger->error($eventType, + $this->logger->error( + $eventType, $this->formatContext( __($source->getMessage()), $this->request->getClientAddress(true), - $userLogin)); + $userLogin + ) + ); } elseif (($eventMessage = $event->getEventMessage()) !== null) { - $this->logger->debug($eventType, + $this->logger->debug( + $eventType, $this->formatContext( $eventMessage->composeText(' | '), $this->request->getClientAddress(true), - $userLogin)); + $userLogin + ) + ); } else { - $this->logger->debug($eventType, + $this->logger->debug( + $eventType, $this->formatContext( 'N/A', $this->request->getClientAddress(true), - $userLogin)); + $userLogin + ) + ); } $this->language->unsetAppLocales(); diff --git a/lib/SP/Repositories/Account/AccountRepository.php b/lib/SP/Repositories/Account/AccountRepository.php index 87d2aebf..ec13f9e9 100644 --- a/lib/SP/Repositories/Account/AccountRepository.php +++ b/lib/SP/Repositories/Account/AccountRepository.php @@ -703,7 +703,9 @@ final class AccountRepository extends Repository implements RepositoryInterface ); if (QueryCondition::CONDITION_AND === $accountSearchFilter->getFilterOperator()) { - $queryData->setGroupBy('Account.id HAVING COUNT(DISTINCT AccountToTag.tagId) = ' . count($accountSearchFilter->getTagsId())); + $groupBy = sprintf('Account.id HAVING COUNT(DISTINCT AccountToTag.tagId) = %d', count($accountSearchFilter->getTagsId())); + + $queryData->setGroupBy($groupBy); } } diff --git a/lib/SP/Services/Account/AccountSearchFilter.php b/lib/SP/Services/Account/AccountSearchFilter.php index c7e914c1..161721ca 100644 --- a/lib/SP/Services/Account/AccountSearchFilter.php +++ b/lib/SP/Services/Account/AccountSearchFilter.php @@ -225,7 +225,11 @@ final class AccountSearchFilter $orderDir = $this->sortOrder === self::SORT_DIR_ASC ? 'ASC' : 'DESC'; - return sprintf('%s %s', implode(',', $orderKey), $orderDir); + return sprintf( + '%s %s', + implode(',', $orderKey), + $orderDir + ); } public function isSortViews(): bool diff --git a/lib/SP/Services/Api/ApiRequest.php b/lib/SP/Services/Api/ApiRequest.php index 7921ff92..b1cc625c 100644 --- a/lib/SP/Services/Api/ApiRequest.php +++ b/lib/SP/Services/Api/ApiRequest.php @@ -41,8 +41,6 @@ final class ApiRequest /** * ApiRequest constructor. * - * @param string|null $request - * * @throws \SP\Services\Api\ApiRequestException */ public function __construct(?string $request = null) diff --git a/lib/SP/Services/Api/ApiService.php b/lib/SP/Services/Api/ApiService.php index 38e33b94..4fbebd0d 100644 --- a/lib/SP/Services/Api/ApiService.php +++ b/lib/SP/Services/Api/ApiService.php @@ -263,9 +263,15 @@ final class ApiService extends Service string $param, bool $required = false, $default = null - ): int + ): ?int { - return Filter::getInt($this->getParam($param, $required, $default)); + $value = $this->getParam($param, $required, $default); + + if (null !== $value) { + return Filter::getInt($value); + } + + return $default; } /** @@ -275,9 +281,15 @@ final class ApiService extends Service string $param, bool $required = false, $default = null - ): string + ): ?string { - return Filter::getString($this->getParam($param, $required, $default)); + $value = $this->getParam($param, $required, $default); + + if (null !== $value) { + return Filter::getString($value); + } + + return $default; } /** @@ -286,13 +298,13 @@ final class ApiService extends Service public function getParamArray( string $param, bool $required = false, - $default = null): - ?array + $default = null + ): ?array { - $array = $this->getParam($param, $required, $default); + $value = $this->getParam($param, $required, $default); - if ($array !== null) { - return Filter::getArray($array); + if (null !== $value) { + return Filter::getArray($value); } return null; @@ -305,9 +317,15 @@ final class ApiService extends Service string $param, bool $required = false, $default = null - ): string + ): ?string { - return Filter::getRaw($this->getParam($param, $required, $default)); + $value = $this->getParam($param, $required, $default); + + if (null !== $value) { + return Filter::getRaw($value); + } + + return $default; } /** diff --git a/lib/SP/Services/AuthToken/AuthTokenService.php b/lib/SP/Services/AuthToken/AuthTokenService.php index 030ffeb6..a64b5033 100644 --- a/lib/SP/Services/AuthToken/AuthTokenService.php +++ b/lib/SP/Services/AuthToken/AuthTokenService.php @@ -169,7 +169,7 @@ final class AuthTokenService extends Service * @throws ConstraintException * @throws QueryException */ - public function create($itemData): int + public function create(AuthTokenData $itemData): int { return $this->authTokenRepository->create($this->injectSecureData($itemData)); } diff --git a/lib/SP/Services/Backup/FileBackupService.php b/lib/SP/Services/Backup/FileBackupService.php index e5a93cd4..926ab536 100644 --- a/lib/SP/Services/Backup/FileBackupService.php +++ b/lib/SP/Services/Backup/FileBackupService.php @@ -52,7 +52,8 @@ defined('APP_ROOT') || die(); */ final class FileBackupService extends Service { - private const BACKUP_EXCLUDE_REGEX = '#^(?!.*(backup|cache|temp|vendor|tests))(.*)$#i'; + private const BACKUP_INCLUDE_REGEX = /** @lang RegExp */ + '#^(?:[A-Z]:)?(?:/(?!(\.git|backup|cache|temp|vendor|tests))[^/]+)+/[^/]+\.\w+$#Di'; private ?ConfigDataInterface $configData = null; private ?string $path = null; @@ -120,7 +121,7 @@ final class FileBackupService extends Service private function checkBackupDir(): void { if (is_dir($this->path) === false - && !@mkdir($concurrentDirectory = $this->path, 0750) + && !@mkdir($concurrentDirectory = $this->path, 0750, true) && !is_dir($concurrentDirectory) ) { throw new ServiceException( @@ -213,7 +214,9 @@ final class FileBackupService extends Service if ($tables === '*') { $resTables = DatabaseUtil::TABLES; } else { - $resTables = is_array($tables) ? $tables : explode(',', $tables); + $resTables = is_array($tables) + ? $tables + : explode(',', $tables); } $lineSeparator = PHP_EOL . PHP_EOL; @@ -338,7 +341,7 @@ final class FileBackupService extends Service $archive = new ArchiveHandler($this->backupFileApp, $this->extensionChecker); - $archive->compressDirectory(APP_ROOT, self::BACKUP_EXCLUDE_REGEX); + $archive->compressDirectory(APP_ROOT, self::BACKUP_INCLUDE_REGEX); return true; } diff --git a/lib/SP/Services/Category/CategoryService.php b/lib/SP/Services/Category/CategoryService.php index 1990298a..7aa11326 100644 --- a/lib/SP/Services/Category/CategoryService.php +++ b/lib/SP/Services/Category/CategoryService.php @@ -101,7 +101,10 @@ final class CategoryService extends Service public function delete(int $id): CategoryService { if ($this->categoryRepository->delete($id) === 0) { - throw new NoSuchItemException(__u('Category not found'), NoSuchItemException::INFO); + throw new NoSuchItemException( + __u('Category not found'), + SPException::INFO + ); } return $this; diff --git a/lib/SP/Services/Export/XmlExportService.php b/lib/SP/Services/Export/XmlExportService.php index 205f01a4..effef94e 100644 --- a/lib/SP/Services/Export/XmlExportService.php +++ b/lib/SP/Services/Export/XmlExportService.php @@ -100,7 +100,7 @@ final class XmlExportService extends Service private function setExportPath(string $exportPath): void { if (!is_dir($exportPath) - && !mkdir($exportPath, 0700, true) + && !@mkdir($exportPath, 0700, true) && !is_dir($exportPath) ) { throw new ServiceException(sprintf( diff --git a/lib/SP/Services/Import/XmlImportBase.php b/lib/SP/Services/Import/XmlImportBase.php index 1029006c..24c5b819 100644 --- a/lib/SP/Services/Import/XmlImportBase.php +++ b/lib/SP/Services/Import/XmlImportBase.php @@ -29,7 +29,6 @@ use DOMElement; use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; use Psr\Container\NotFoundExceptionInterface; -use SP\Config\ConfigData; use SP\Config\ConfigDataInterface; use SP\Core\Events\EventDispatcher; use SP\Core\Exceptions\SPException; @@ -70,7 +69,7 @@ abstract class XmlImportBase $this->importParams = $importParams; $this->xmlDOM = $xmlFileImport->getXmlDOM(); - $this->configData = $dic->get(ConfigData::class); + $this->configData = $dic->get(ConfigDataInterface::class); $this->accountService = $dic->get(AccountService::class); $this->categoryService = $dic->get(CategoryService::class); $this->clientService = $dic->get(ClientService::class); diff --git a/lib/SP/Services/Install/Installer.php b/lib/SP/Services/Install/Installer.php index fbc6c0e4..2ef98ea3 100644 --- a/lib/SP/Services/Install/Installer.php +++ b/lib/SP/Services/Install/Installer.php @@ -125,7 +125,7 @@ final class Installer extends Service __u('An user with database administrative rights')); } - if (APP_MODULE !== 'tests' + if (IS_TESTING && empty($this->installData->getDbAdminPass())) { throw new InvalidArgumentException( __u('Please, enter the database password'), @@ -207,7 +207,7 @@ final class Installer extends Service if (strpos('localhost', $this->installData->getDbHost()) === false && strpos('127.0.0.1', $this->installData->getDbHost()) === false ) { - if (APP_MODULE === 'tests') { + if (defined('SELF_IP_ADDRESS')) { $address = SELF_IP_ADDRESS; } else { $address = $this->request->getServer('SERVER_ADDR'); diff --git a/lib/SP/Services/Upgrade/UpgradeConfigService.php b/lib/SP/Services/Upgrade/UpgradeConfigService.php index 1369855f..ed1abee9 100644 --- a/lib/SP/Services/Upgrade/UpgradeConfigService.php +++ b/lib/SP/Services/Upgrade/UpgradeConfigService.php @@ -195,8 +195,6 @@ final class UpgradeConfigService extends Service implements UpgradeInterface /** * Migrar valores de configuración. - * - * @throws FileException */ public function upgrade(string $version, ConfigDataInterface $configData): void { diff --git a/lib/SP/Storage/Database/DatabaseConnectionData.php b/lib/SP/Storage/Database/DatabaseConnectionData.php index 2e8c26da..3ecb4995 100644 --- a/lib/SP/Storage/Database/DatabaseConnectionData.php +++ b/lib/SP/Storage/Database/DatabaseConnectionData.php @@ -53,6 +53,17 @@ final class DatabaseConnectionData ->setDbSocket($configData->getDbSocket()); } + public static function getFromEnvironment(): DatabaseConnectionData + { + return (new self()) + ->setDbHost(getenv('DB_SERVER')) + ->setDbName(getenv('DB_NAME')) + ->setDbUser(getenv('DB_USER')) + ->setDbPass(getenv('DB_PASS')) + ->setDbPort((int)getenv('DB_PORT')) + ->setDbSocket(getenv('DB_SOCKET')); + } + public function refreshFromConfig( ConfigDataInterface $configData ): DatabaseConnectionData diff --git a/lib/SP/Storage/File/FileHandler.php b/lib/SP/Storage/File/FileHandler.php index a7f223ba..5c137634 100644 --- a/lib/SP/Storage/File/FileHandler.php +++ b/lib/SP/Storage/File/FileHandler.php @@ -328,7 +328,7 @@ final class FileHandler */ public function delete(): FileHandler { - if (@unlink($this->file) === false) { + if (file_exists($this->file) && @unlink($this->file) === false) { throw new FileException(sprintf( __('Unable to delete file (%s)'), $this->file diff --git a/lib/SP/Util/Filter.php b/lib/SP/Util/Filter.php index 9976573b..21139a73 100644 --- a/lib/SP/Util/Filter.php +++ b/lib/SP/Util/Filter.php @@ -67,9 +67,11 @@ final class Filter /** * @param string|int $value */ - public static function getInt($value): int + public static function getInt($value): ?int { - return (int)filter_var($value, FILTER_SANITIZE_NUMBER_INT); + $filterVar = filter_var($value, FILTER_SANITIZE_NUMBER_INT); + + return is_numeric($filterVar) ? (int)$filterVar : null; } public static function getString(?string $value): string diff --git a/lib/SP/Util/Link.php b/lib/SP/Util/Link.php index fea8d659..58615d33 100644 --- a/lib/SP/Util/Link.php +++ b/lib/SP/Util/Link.php @@ -47,9 +47,9 @@ final class Link $route = Acl::getActionRoute($actionId) . '/' . $itemId; if ($useUI) { - $baseUrl = ($configData->getApplicationUrl() ?: Bootstrap::$WEBURI) . '/index.php'; + $baseUrl = ($configData->getApplicationUrl() ?? Bootstrap::$WEBURI) . '/index.php'; } else { - $baseUrl = ($configData->getApplicationUrl() ?: Bootstrap::$WEBURI) . Bootstrap::$SUBURI; + $baseUrl = ($configData->getApplicationUrl() ?? Bootstrap::$WEBURI) . Bootstrap::$SUBURI; } $uri = new Uri($baseUrl); diff --git a/lib/SplClassLoader.php b/lib/SplClassLoader.php deleted file mode 100644 index 26fba5e6..00000000 --- a/lib/SplClassLoader.php +++ /dev/null @@ -1,181 +0,0 @@ -. - */ - -/** - * SplClassLoader implementation that implements the technical interoperability - * standards for PHP 5.3 namespaces and class names. - * - * http://groups.google.com/group/php-standards/web/psr-0-final-proposal?pli=1 - * - * // Example which loads classes for the Doctrine Common package in the - * // Doctrine\Common namespace. - * $classLoader = new SplClassLoader('Doctrine\Common', '/path/to/doctrine'); - * $classLoader->register(); - * - * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @author Jonathan H. Wage - * @author Roman S. Borschel - * @author Matthew Weier O'Phinney - * @author Kris Wallsmith - * @author Fabien Potencier - */ -class SplClassLoader -{ - private $_fileExtension = '.php'; - private $_namespace; - private $_includePath; - private $_namespaceSeparator = '\\'; - private $_prepend = false; - private $_excluded = array(); - - /** - * Creates a new SplClassLoader that loads classes of the - * specified namespace. - * - * @param string $ns The namespace to use. - * @param string $includePath - */ - public function __construct($ns = null, $includePath = null) - { - $this->_namespace = $ns; - $this->_includePath = $includePath; - } - - /** - * @param string $excluded - */ - public function addExcluded($excluded) - { - $this->_excluded[] = $excluded; - } - - /** - * @param boolean $prepend - */ - public function setPrepend($prepend) - { - $this->_prepend = $prepend; - } - - /** - * Gets the namespace seperator used by classes in the namespace of this class loader. - * - * @return void - */ - public function getNamespaceSeparator() - { - return $this->_namespaceSeparator; - } - - /** - * Sets the namespace separator used by classes in the namespace of this class loader. - * - * @param string $sep The separator to use. - */ - public function setNamespaceSeparator($sep) - { - $this->_namespaceSeparator = $sep; - } - - /** - * Gets the base include path for all class files in the namespace of this class loader. - * - * @return string $includePath - */ - public function getIncludePath() - { - return $this->_includePath; - } - - /** - * Sets the base include path for all class files in the namespace of this class loader. - * - * @param string $includePath - */ - public function setIncludePath($includePath) - { - $this->_includePath = $includePath; - } - - /** - * Gets the file extension of class files in the namespace of this class loader. - * - * @return string $fileExtension - */ - public function getFileExtension() - { - return $this->_fileExtension; - } - - /** - * Sets the file extension of class files in the namespace of this class loader. - * - * @param string $fileExtension - */ - public function setFileExtension($fileExtension) - { - $this->_fileExtension = $fileExtension; - } - - /** - * Installs this class loader on the SPL autoload stack. - */ - public function register() - { - spl_autoload_register([$this, 'loadClass'], true, $this->_prepend); - } - - /** - * Uninstalls this class loader from the SPL autoloader stack. - */ - public function unregister() - { - spl_autoload_unregister([$this, 'loadClass']); - } - - /** - * Loads the given class or interface. - * - * @param string $className The name of the class to load. - * @return void - */ - public function loadClass($className) - { - if (!in_array($className, $this->_excluded) && - (null === $this->_namespace - || 0 === strpos($className, $this->_namespace . $this->_namespaceSeparator)) - ) { - $fileName = ''; - if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) { - $namespace = substr($className, 0, $lastNsPos); - $className = substr($className, $lastNsPos + 1); - $fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; - } - - $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension; - - require ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName; - } - } -} \ No newline at end of file diff --git a/tests/SP/DatabaseTestCase.php b/tests/SP/DatabaseTestCase.php index f72ee142..79bd6847 100644 --- a/tests/SP/DatabaseTestCase.php +++ b/tests/SP/DatabaseTestCase.php @@ -26,6 +26,8 @@ namespace SP\Tests; use PHPUnit\Framework\TestCase; +define('APP_MODULE', 'web-test'); + /** * Class DatabaseBaseTest * diff --git a/tests/SP/DatabaseTrait.php b/tests/SP/DatabaseTrait.php index 22e0131d..174d7ee5 100644 --- a/tests/SP/DatabaseTrait.php +++ b/tests/SP/DatabaseTrait.php @@ -38,7 +38,7 @@ trait DatabaseTrait protected static function getRowCount(string $table): int { if (!self::$conn) { - return 0; + self::setConnection(); } $sql = sprintf('SELECT count(*) FROM `%s`', $table); @@ -46,6 +46,19 @@ trait DatabaseTrait return (int)self::$conn->query($sql)->fetchColumn(); } + protected static function setConnection(): void + { + if (!self::$conn) { + try { + self::$conn = getDbHandler()->getConnection(); + } catch (DatabaseException $e) { + processException($e); + + exit(1); + } + } + } + protected static function loadFixtures(): void { $dbServer = getenv('DB_SERVER'); @@ -88,15 +101,16 @@ trait DatabaseTrait printf('Fixtures loaded from: %s' . PHP_EOL, $file); } + } + protected static function truncateTable(string $table): void + { if (!self::$conn) { - try { - self::$conn = getDbHandler()->getConnection(); - } catch (DatabaseException $e) { - processException($e); - - exit(1); - } + self::setConnection(); } + + $sql = sprintf('TRUNCATE TABLE `%s`', $table); + + self::$conn->exec($sql); } } \ No newline at end of file diff --git a/tests/SP/Modules/Api/ApiTest.php b/tests/SP/Modules/Api/ApiTest.php deleted file mode 100644 index ac68293b..00000000 --- a/tests/SP/Modules/Api/ApiTest.php +++ /dev/null @@ -1,94 +0,0 @@ -. - */ - -namespace SP\Tests\Modules\Api; - -use SP\Services\Api\JsonRpcResponse; -use SP\Tests\WebTestCase; -use stdClass; - -/** - * Class ApiTest - * - * @package SP\Tests\SP\Modules\Api - */ -class ApiTest extends WebTestCase -{ - - const API_TOKEN = '4eb7a989fab4c8fd9ade0ea80df7032d5ee78d4496c1c10f9c4388a872bfff28'; - const API_PASS = ')%Iykm*4A]wg'; - const API_URL = 'http://syspass-app-test/api.php'; - - public function testInvalidRequest() - { - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL), 404); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals('2.0', $result->jsonrpc); - $this->assertEquals(JsonRpcResponse::INVALID_REQUEST, $result->error->code); - $this->assertNull($result->error->data); - $this->assertEquals(0, $result->id); - } - - public function testNoInvalidToken() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => 'this_is_a_test' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals('2.0', $result->jsonrpc); - $this->assertEquals('Internal error', $result->error->message); - $this->assertEquals(0, $result->error->code); - $this->assertNull($result->error->data); - $this->assertEquals(1, $result->id); - } - - public function testNoInvalidMethod() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'test/test', - 'params' => [ - 'authToken' => 'this_is_a_test' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data), 404); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals('2.0', $result->jsonrpc); - $this->assertEquals(JsonRpcResponse::METHOD_NOT_FOUND, $result->error->code); - $this->assertNull($result->error->data); - $this->assertEquals(1, $result->id); - } -} \ No newline at end of file diff --git a/tests/SP/Modules/Api/ApiTestCase.php b/tests/SP/Modules/Api/ApiTestCase.php new file mode 100644 index 00000000..8090b1f8 --- /dev/null +++ b/tests/SP/Modules/Api/ApiTestCase.php @@ -0,0 +1,226 @@ +. + */ + +namespace SP\Tests\Modules\Api; + +use DI\ContainerBuilder; +use Klein\Request; +use Klein\Response; +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; +use RuntimeException; +use SP\Bootstrap; +use SP\Config\Config; +use SP\Config\ConfigDataInterface; +use SP\Core\Acl\ActionsInterface; +use SP\Core\Context\ContextInterface; +use SP\DataModel\AuthTokenData; +use SP\Services\Api\ApiRequest; +use SP\Services\AuthToken\AuthTokenService; +use SP\Storage\Database\DatabaseConnectionData; +use SP\Storage\Database\DBStorageInterface; +use SP\Storage\Database\MySQLHandler; +use SP\Tests\DatabaseTrait; +use stdClass; +use function DI\create; +use const SP\Tests\APP_DEFINITIONS_FILE; + +define('APP_MODULE', 'api'); + +/** + * Class WebTestCase + */ +abstract class ApiTestCase extends TestCase +{ + private const AUTH_TOKEN_PASS = 123456; + + use DatabaseTrait; + + private const METHOD_ACTION_MAP = [ + ActionsInterface::ACCOUNT_CREATE => 'account/create', + ActionsInterface::ACCOUNT_VIEW => 'account/view', + ActionsInterface::ACCOUNT_VIEW_PASS => 'account/viewPass', + ActionsInterface::ACCOUNT_EDIT_PASS => 'account/editPass', + ActionsInterface::ACCOUNT_EDIT => 'account/edit', + ActionsInterface::ACCOUNT_SEARCH => 'account/search', + ActionsInterface::ACCOUNT_DELETE => 'account/delete', + ActionsInterface::CATEGORY_VIEW => 'category/view', + ActionsInterface::CATEGORY_CREATE => 'category/create', + ActionsInterface::CATEGORY_EDIT => 'category/edit', + ActionsInterface::CATEGORY_DELETE => 'category/delete', + ActionsInterface::CATEGORY_SEARCH => 'category/search', + ActionsInterface::CLIENT_VIEW => 'client/view', + ActionsInterface::CLIENT_CREATE => 'client/create', + ActionsInterface::CLIENT_EDIT => 'client/edit', + ActionsInterface::CLIENT_DELETE => 'client/delete', + ActionsInterface::CLIENT_SEARCH => 'client/search', + ActionsInterface::TAG_VIEW => 'tag/view', + ActionsInterface::TAG_CREATE => 'tag/create', + ActionsInterface::TAG_EDIT => 'tag/edit', + ActionsInterface::TAG_DELETE => 'tag/delete', + ActionsInterface::TAG_SEARCH => 'tag/search', + ActionsInterface::GROUP_VIEW => 'userGroup/view', + ActionsInterface::GROUP_CREATE => 'userGroup/create', + ActionsInterface::GROUP_EDIT => 'userGroup/edit', + ActionsInterface::GROUP_DELETE => 'userGroup/delete', + ActionsInterface::GROUP_SEARCH => 'userGroup/search', + ActionsInterface::CONFIG_BACKUP_RUN => 'config/backup', + ActionsInterface::CONFIG_EXPORT_RUN => 'config/export', + ]; + protected static ?ConfigDataInterface $configData = null; + + /** + * @throws \JsonException + */ + protected static function processJsonResponse( + Response $response, + bool $exceptionOnError = true + ): stdClass + { + if ($exceptionOnError && $response->status()->getCode() !== 200) { + throw new RuntimeException($response->status()->getMessage()); + } + + return json_decode( + $response->body(), + false, + 512, + JSON_THROW_ON_ERROR + ); + } + + protected function setUp(): void + { + self::loadFixtures(); + + self::truncateTable('AuthToken'); + + parent::setUp(); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \Exception + */ + final protected function callApi(int $actionId, array $params): Response + { + $databaseConnectionData = DatabaseConnectionData::getFromEnvironment(); + + $dic = (new ContainerBuilder()) + ->addDefinitions( + APP_DEFINITIONS_FILE, + [ + ApiRequest::class => function (ContainerInterface $c) use ($actionId, $params) { + $token = self::createApiToken( + $c->get(AuthTokenService::class), + $actionId + ); + + $data = [ + 'jsonrpc' => '2.0', + 'method' => self::METHOD_ACTION_MAP[$actionId], + 'params' => array_merge( + [ + 'authToken' => $token->getToken(), + 'tokenPass' => self::AUTH_TOKEN_PASS, + ], + $params + ), + 'id' => 1 + ]; + + return new ApiRequest(json_encode($data, JSON_THROW_ON_ERROR)); + }, + DBStorageInterface::class => create(MySQLHandler::class) + ->constructor($databaseConnectionData), + ConfigDataInterface::class => static function (Config $config) use ($databaseConnectionData) { + $configData = $config->getConfigData() + ->setDbHost($databaseConnectionData->getDbHost()) + ->setDbName($databaseConnectionData->getDbName()) + ->setDbUser($databaseConnectionData->getDbUser()) + ->setDbPass($databaseConnectionData->getDbPass()); + + // Update ConfigData instance + $config->updateConfig($configData); + + return $configData; + } + ] + ) + ->build(); + + $context = $dic->get(ContextInterface::class); + $context->initialize(); + $context->setTrasientKey('_masterpass', '12345678900'); + + self::$configData = $dic->get(ConfigDataInterface::class); + + $request = new Request( + [], + [], + [], + [ + 'HTTP_HOST' => 'localhost:8080', + 'HTTP_ACCEPT' => 'application/json, text/javascript, */*; q=0.01', + 'HTTP_USER_AGENT' => 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0', + 'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.5', + 'HTTP_ACCEPT_ENCODING' => 'gzip, deflate', + 'REQUEST_URI' => '/api.php', + 'REQUEST_METHOD' => 'POST', + 'HTTP_CONTENT_TYPE' => 'application/json' + ], + [], + null + ); + + $router = (new Bootstrap($dic))->getRouter(); + $router->dispatch($request, null, false); + + return $router->response(); + } + + /** + * @throws \SP\Core\Exceptions\QueryException + * @throws \SP\Core\Exceptions\ConstraintException + * @throws \Defuse\Crypto\Exception\CryptoException + * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException + * @throws \SP\Core\Exceptions\SPException + */ + private static function createApiToken( + AuthTokenService $service, + int $actionId + ): AuthTokenData + { + $data = new AuthTokenData(); + $data->setActionId($actionId); + $data->setCreatedBy(1); + $data->setHash(self::AUTH_TOKEN_PASS); + $data->setUserId(1); + + $service->create($data); + + return $data; + } +} \ No newline at end of file diff --git a/tests/SP/Modules/Api/Controllers/AccountControllerTest.php b/tests/SP/Modules/Api/Controllers/AccountControllerTest.php index 584a0297..e6704be2 100644 --- a/tests/SP/Modules/Api/Controllers/AccountControllerTest.php +++ b/tests/SP/Modules/Api/Controllers/AccountControllerTest.php @@ -2,8 +2,8 @@ /** * sysPass * - * @author nuxsmin - * @link https://syspass.org + * @author nuxsmin + * @link https://syspass.org * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. @@ -24,8 +24,9 @@ namespace SP\Tests\Modules\Api\Controllers; -use SP\Tests\Modules\Api\ApiTest; -use SP\Tests\WebTestCase; +use SP\Core\Acl\ActionsInterface; +use SP\Modules\Api\Controllers\AccountController; +use SP\Tests\Modules\Api\ApiTestCase; use stdClass; /** @@ -33,697 +34,729 @@ use stdClass; * * @package SP\Modules\Api\Controllers */ -class AccountControllerTest extends WebTestCase +class AccountControllerTest extends ApiTestCase { + private const PARAMS = [ + 'name' => 'API test', + 'categoryId' => 2, + 'clientId' => 2, + 'login' => 'root', + 'pass' => 'password_test', + 'expireDate' => 1634395912, + 'url' => 'http://syspass.org', + 'notes' => "test\n\ntest", + 'private' => 0, + 'privateGroup' => 0, + 'userId' => 2, + 'userGroupId' => 2, + 'parentId' => 0, + 'tagsId' => [3] + ]; + + protected AccountController $controller; + /** - * @return int + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testCreateAction() + public function testCreateAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/create', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'tokenPass' => ApiTest::API_PASS, - 'name' => 'API test', - 'categoryId' => 2, - 'clientId' => 2, - 'login' => 'root', - 'pass' => 'password_test', - 'expireDate' => time() + 86400, - 'url' => 'http://syspass.org', - 'notes' => "test\n\ntest", - 'isPrivate' => 1, - 'isPrivateGroup' => 1, - 'userId' => 1, - 'userGroupId' => 1 - ], - 'id' => 1 - ]; + $params = self::PARAMS; + $params['private'] = 1; + $params['privateGroup'] = 1; + $params['parentId'] = 1; - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = $this->createAccount($params); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertInstanceOf(stdClass::class, $result->result); - $this->assertEquals(3, $result->result->itemId); - $this->assertEquals('Account created', $result->result->resultMessage); + $this->assertEquals(0, $response->result->resultCode); + $this->assertNull($response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals(5, $response->result->itemId); + $this->assertEquals('Account created', $response->result->resultMessage); - return $result->result->itemId; - } + $resultItem = $response->result->result; - public function testCreateActionNoUserData() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/create', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'tokenPass' => ApiTest::API_PASS, - 'name' => 'API test', - 'categoryId' => 2, - 'clientId' => 2, - 'login' => 'root', - 'pass' => 'password_test', - 'expireDate' => time() + 86400, - 'url' => 'http://syspass.org', - 'notes' => "test\n\ntest", - 'isPrivate' => 1, - 'isPrivateGroup' => 1 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertInstanceOf(stdClass::class, $result->result); - $this->assertEquals(4, $result->result->itemId); - $this->assertEquals('Account created', $result->result->resultMessage); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/delete', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $result->result->itemId, - ], - 'id' => 1 - ]; - - self::postJson(ApiTest::API_URL, $data); + $this->assertEquals($response->result->itemId, $resultItem->id); + $this->assertEquals(self::PARAMS['name'], $resultItem->name); + $this->assertEquals(self::PARAMS['categoryId'], $resultItem->categoryId); + $this->assertEquals(self::PARAMS['clientId'], $resultItem->clientId); + $this->assertEquals(self::PARAMS['login'], $resultItem->login); + $this->assertEquals(self::PARAMS['expireDate'], $resultItem->passDateChange); + $this->assertEquals(self::PARAMS['url'], $resultItem->url); + $this->assertEquals(self::PARAMS['notes'], $resultItem->notes); + $this->assertEquals(self::PARAMS['userId'], $resultItem->userId); + $this->assertEquals(self::PARAMS['userGroupId'], $resultItem->userGroupId); + $this->assertEquals($params['private'], $resultItem->isPrivate); + $this->assertEquals($params['privateGroup'], $resultItem->isPrivateGroup); + $this->assertEquals($params['parentId'], $resultItem->parentId); + $this->assertEmpty($resultItem->pass); + $this->assertEmpty($resultItem->key); + $this->assertNull($resultItem->dateEdit); + $this->assertEquals(0, $resultItem->countView); + $this->assertEquals(0, $resultItem->countDecrypt); + $this->assertGreaterThan(0, $resultItem->passDate); + $this->assertEquals(self::PARAMS['expireDate'], $resultItem->passDateChange); } /** - * @depends testCreateAction + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + private function createAccount(?array $params = null): stdClass + { + $api = $this->callApi( + ActionsInterface::ACCOUNT_CREATE, + $params ?? self::PARAMS + ); + + return self::processJsonResponse($api); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testCreateActionNoUserData(): void + { + $params = self::PARAMS; + + unset($params['userId'], $params['userGroupId']); + + $response = $this->createAccount($params); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertNull($response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals(5, $response->result->itemId); + $this->assertEquals('Account created', $response->result->resultMessage); + + $resultItem = $response->result->result; + + $this->assertEquals($response->result->itemId, $resultItem->id); + $this->assertEquals($params['name'], $resultItem->name); + $this->assertEquals($params['categoryId'], $resultItem->categoryId); + $this->assertEquals($params['clientId'], $resultItem->clientId); + $this->assertEquals($params['login'], $resultItem->login); + $this->assertEquals($params['expireDate'], $resultItem->passDateChange); + $this->assertEquals($params['url'], $resultItem->url); + $this->assertEquals($params['notes'], $resultItem->notes); + $this->assertEquals($params['private'], $resultItem->isPrivate); + $this->assertEquals($params['privateGroup'], $resultItem->isPrivateGroup); + $this->assertEquals(1, $resultItem->userId); + $this->assertEquals(1, $resultItem->userGroupId); + } + + /** + * @dataProvider getUnsetParams * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testViewPassAction($id) + public function testCreateActionRequiredParameters(string $unsetParam): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/viewPass', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'tokenPass' => ApiTest::API_PASS, - 'id' => $id, - ], - 'id' => 1 - ]; + $params = self::PARAMS; - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + unset($params[$unsetParam]); + + $response = $this->createAccount($params); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertInstanceOf(stdClass::class, $result->result->result); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('password_test', $result->result->result->password); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testEditPassAction($id) + public function testViewPassAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/editPass', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'tokenPass' => ApiTest::API_PASS, + $response = $this->createAccount(); + + $id = $response->result->itemId; + + $api = $this->callApi( + ActionsInterface::ACCOUNT_VIEW_PASS, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result; + + $this->assertEquals(self::PARAMS['pass'], $resultItem->password); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testViewPassActionRequiredParamater(): void + { + $api = $this->callApi( + ActionsInterface::ACCOUNT_VIEW_PASS, + [] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditPassAction(): void + { + $response = $this->createAccount(); + + $id = $response->result->itemId; + + $api = $this->callApi( + ActionsInterface::ACCOUNT_EDIT_PASS, + [ 'id' => $id, 'pass' => 'test_123', 'expireDate' => time() + 86400 - ], - 'id' => 1 - ]; + ] + ); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = self::processJsonResponse($api); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertInstanceOf(stdClass::class, $result->result->result); - $this->assertEquals('Password updated', $result->result->resultMessage); - $this->assertEquals($id, $result->result->itemId); + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals('Password updated', $response->result->resultMessage); + $this->assertEquals($id, $response->result->itemId); - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/viewPass', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'tokenPass' => ApiTest::API_PASS, - 'id' => $id, - ], - 'id' => 1 - ]; + $api = $this->callApi( + ActionsInterface::ACCOUNT_VIEW_PASS, + ['id' => $id] + ); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = self::processJsonResponse($api); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertInstanceOf(stdClass::class, $result->result->result); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('test_123', $result->result->result->password); + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result; + + $this->assertEquals('test_123', $resultItem->password); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testViewAction($id) + public function testEditPassActionRequiredParameters(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/view', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - ], - 'id' => 1 - ]; + $response = $this->createAccount(); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $id = $response->result->itemId; - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals($id, $result->result->itemId); - $this->assertNull($result->result->count); + $api = $this->callApi( + ActionsInterface::ACCOUNT_EDIT_PASS, + [ + 'id' => $id + ] + ); - $this->assertInstanceOf(stdClass::class, $result->result->result); - $this->assertEquals($id, $result->result->result->id); - $this->assertEquals(1, $result->result->result->userId); - $this->assertEquals(1, $result->result->result->userGroupId); - $this->assertEquals(1, $result->result->result->userEditId); - $this->assertEquals('API test', $result->result->result->name); - $this->assertEquals(2, $result->result->result->clientId); - $this->assertEquals(2, $result->result->result->categoryId); - $this->assertEquals('root', $result->result->result->login); - $this->assertEquals('http://syspass.org', $result->result->result->url); - $this->assertEmpty($result->result->result->pass); - $this->assertEmpty($result->result->result->key); - $this->assertEquals("test\n\ntest", $result->result->result->notes); - $this->assertNotNull($result->result->result->dateEdit); - $this->assertEquals(0, $result->result->result->countView); - $this->assertEquals(2, $result->result->result->countDecrypt); - $this->assertEquals(0, $result->result->result->isPrivate); - $this->assertEquals(0, $result->result->result->isPrivateGroup); - $this->assertGreaterThan(0, $result->result->result->passDate); - $this->assertGreaterThan(0, $result->result->result->passDateChange); - $this->assertEquals(0, $result->result->result->parentId); - } + $response = self::processJsonResponse($api); - public function testSearchAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(3, $result->result->count); - $this->assertCount(3, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'count' => 1 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - } - - public function testSearchByTextAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'text' => 'Simple' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'text' => 'admin' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(2, $result->result->count); - $this->assertCount(2, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'text' => 'cloud' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - } - - public function testSearchByClientAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'clientId' => 2 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'clientId' => 3 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'clientId' => 10 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(0, $result->result->count); - $this->assertCount(0, $result->result->result); - } - - public function testSearchByCategoryAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'categoryId' => 1 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'categoryId' => 3 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(0, $result->result->count); - $this->assertCount(0, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'categoryId' => 10 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(0, $result->result->count); - $this->assertCount(0, $result->result->result); - } - - public function testSearchByTagsAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'tagsId' => [3] - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'tagsId' => [3, 6] - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(0, $result->result->count); - $this->assertCount(0, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'tagsId' => [3, 6], - 'op' => 'or' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(2, $result->result->count); - $this->assertCount(2, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'tagsId' => [10] - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(0, $result->result->count); - $this->assertCount(0, $result->result->result); - } - - public function testSearchByCategoryAndClientAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'categoryId' => 1, - 'clientId' => 1 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'categoryId' => 2, - 'clientId' => 3 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'categoryId' => 2, - 'clientId' => 1 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(0, $result->result->count); - $this->assertCount(0, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'categoryId' => 1, - 'clientId' => 3, - 'op' => 'or' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(2, $result->result->count); - $this->assertCount(2, $result->result->result); + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testEditAction($id) + public function testViewPassActionNonExistant(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/edit', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - 'name' => 'API test edit', - 'categoryId' => 3, - 'clientId' => 1, - 'login' => 'admin', - 'expireDate' => time() + 86400, - 'url' => 'http://demo.syspass.org', - 'notes' => "test\n\ntest\nedit", - 'isPrivate' => 0, - 'isPrivateGroup' => 0, - 'userId' => 1, - 'userGroupId' => 1 - ], - 'id' => 1 - ]; + $api = $this->callApi( + ActionsInterface::ACCOUNT_VIEW_PASS, + ['id' => 10] + ); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = self::processJsonResponse($api); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertInstanceOf(stdClass::class, $result->result); - $this->assertEquals(3, $result->result->itemId); - $this->assertEquals('Account updated', $result->result->resultMessage); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/view', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals($id, $result->result->itemId); - $this->assertNull($result->result->count); - - $this->assertInstanceOf(stdClass::class, $result->result->result); - $this->assertEquals($id, $result->result->result->id); - $this->assertEquals(1, $result->result->result->userId); - $this->assertEquals(1, $result->result->result->userGroupId); - $this->assertEquals(1, $result->result->result->userEditId); - $this->assertEquals('API test edit', $result->result->result->name); - $this->assertEquals(1, $result->result->result->clientId); - $this->assertEquals(3, $result->result->result->categoryId); - $this->assertEquals('admin', $result->result->result->login); - $this->assertEquals('http://demo.syspass.org', $result->result->result->url); - $this->assertEmpty($result->result->result->pass); - $this->assertEmpty($result->result->result->key); - $this->assertEquals("test\n\ntest\nedit", $result->result->result->notes); - $this->assertNotNull($result->result->result->dateEdit); - $this->assertEquals(0, $result->result->result->isPrivate); - $this->assertEquals(0, $result->result->result->isPrivateGroup); - $this->assertGreaterThan(0, $result->result->result->passDate); - $this->assertGreaterThan(0, $result->result->result->passDateChange); - $this->assertEquals(0, $result->result->result->parentId); + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Account not found', $response->error->message); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testEditActionNoUserData($id) + public function testViewAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/edit', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - 'name' => 'API test edit', - 'categoryId' => 3, - 'clientId' => 1, - 'login' => 'admin', - 'expireDate' => time() + 86400, - 'url' => 'http://demo.syspass.org', - 'notes' => "test\n\ntest\nedit", - 'isPrivate' => 0, - 'isPrivateGroup' => 0 - ], - 'id' => 1 - ]; + $response = $this->createAccount(); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $id = $response->result->itemId; - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertInstanceOf(stdClass::class, $result->result); - $this->assertEquals(3, $result->result->itemId); - $this->assertEquals('Account updated', $result->result->resultMessage); + $api = $this->callApi( + ActionsInterface::ACCOUNT_VIEW, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result->data; + + $this->assertInstanceOf(stdClass::class, $resultItem); + $this->assertEquals($id, $resultItem->id); + $this->assertEquals(self::PARAMS['name'], $resultItem->name); + $this->assertEquals(self::PARAMS['categoryId'], $resultItem->categoryId); + $this->assertEquals(self::PARAMS['clientId'], $resultItem->clientId); + $this->assertEquals(self::PARAMS['login'], $resultItem->login); + $this->assertEquals(self::PARAMS['expireDate'], $resultItem->passDateChange); + $this->assertEquals(self::PARAMS['url'], $resultItem->url); + $this->assertEquals(self::PARAMS['notes'], $resultItem->notes); + $this->assertEquals(self::PARAMS['private'], $resultItem->isPrivate); + $this->assertEquals(self::PARAMS['privateGroup'], $resultItem->isPrivateGroup); + $this->assertEquals(self::PARAMS['userId'], $resultItem->userId); + $this->assertEquals(self::PARAMS['userGroupId'], $resultItem->userGroupId); + $this->assertNull($resultItem->publicLinkHash); + $this->assertEquals(0, $resultItem->dateEdit); + $this->assertEquals(0, $resultItem->countView); + $this->assertEquals(0, $resultItem->countDecrypt); + $this->assertEquals(0, $resultItem->isPrivate); + $this->assertEquals(0, $resultItem->isPrivateGroup); + $this->assertGreaterThan(0, $resultItem->passDate); + $this->assertEquals(self::PARAMS['expireDate'], $resultItem->passDateChange); + $this->assertEquals(self::PARAMS['parentId'], $resultItem->parentId); + $this->assertIsArray($resultItem->tags); + $this->assertCount(1, $resultItem->tags); + $this->assertEquals(self::PARAMS['tagsId'][0], $resultItem->tags[0]->id); + $this->assertEquals('Linux', $resultItem->tags[0]->name); + $this->assertIsArray($resultItem->users); + $this->assertCount(0, $resultItem->users); + $this->assertIsArray($resultItem->userGroups); + $this->assertCount(0, $resultItem->userGroups); + $this->assertNull($resultItem->customFields); + $this->assertIsArray($resultItem->links); + $this->assertEquals('self', $resultItem->links[0]->rel); + $this->assertNotEmpty($resultItem->links[0]->uri); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testDeleteAction($id) + public function testViewActionNonExistant(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'account/delete', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - ], - 'id' => 1 + $api = $this->callApi( + ActionsInterface::ACCOUNT_VIEW, + ['id' => 10] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('The account doesn\'t exist', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testViewActionRequiredParameter(): void + { + $api = $this->callApi( + ActionsInterface::ACCOUNT_VIEW, + [] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + } + + /** + * @dataProvider searchProvider + * + * @throws \DI\DependencyException + * @throws \JsonException + * @throws \DI\NotFoundException + */ + public function testSearchActionByFilter(array $filter, int $resultsCount): void + { + $api = $this->callApi( + ActionsInterface::ACCOUNT_SEARCH, + $filter + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals($resultsCount, $response->result->count); + $this->assertCount($resultsCount, $response->result->result); + } + + /** + * @throws \DI\DependencyException + * @throws \JsonException + * @throws \DI\NotFoundException + */ + public function testEditAction(): void + { + $response = $this->createAccount(); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id, + 'name' => 'API test edit', + 'categoryId' => 3, + 'clientId' => 3, + 'login' => 'admin', + 'expireDate' => time() + 86400, + 'url' => 'http://demo.syspass.org', + 'notes' => "test\n\ntest\nedit", + 'private' => 0, + 'privateGroup' => 0, + 'userId' => 1, + 'userGroupId' => 1, + 'parentId' => 1, + 'tagsId' => [1] ]; - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $api = $this->callApi( + ActionsInterface::ACCOUNT_EDIT, + $params + ); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertInstanceOf(stdClass::class, $result->result); - $this->assertEquals('Account removed', $result->result->resultMessage); - $this->assertEquals($id, $result->result->itemId); - $this->assertNull($result->result->count); + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + $this->assertEquals('Account updated', $response->result->resultMessage); + + $api = $this->callApi( + ActionsInterface::ACCOUNT_VIEW, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result->data; + + $this->assertInstanceOf(stdClass::class, $resultItem); + $this->assertEquals($id, $resultItem->id); + $this->assertEquals($params['name'], $resultItem->name); + $this->assertEquals($params['categoryId'], $resultItem->categoryId); + $this->assertEquals($params['clientId'], $resultItem->clientId); + $this->assertEquals($params['login'], $resultItem->login); + $this->assertEquals($params['expireDate'], $resultItem->passDateChange); + $this->assertEquals($params['url'], $resultItem->url); + $this->assertEquals($params['notes'], $resultItem->notes); + $this->assertEquals($params['private'], $resultItem->isPrivate); + $this->assertEquals($params['privateGroup'], $resultItem->isPrivateGroup); + $this->assertEquals($params['userId'], $resultItem->userId); + $this->assertEquals($params['userGroupId'], $resultItem->userGroupId); + $this->assertNull($resultItem->publicLinkHash); + $this->assertGreaterThan(0, $resultItem->dateEdit); + $this->assertEquals(0, $resultItem->countView); + $this->assertEquals(0, $resultItem->countDecrypt); + $this->assertEquals(0, $resultItem->isPrivate); + $this->assertEquals(0, $resultItem->isPrivateGroup); + $this->assertGreaterThan(0, $resultItem->passDate); + $this->assertEquals($params['expireDate'], $resultItem->passDateChange); + $this->assertEquals($params['parentId'], $resultItem->parentId); + $this->assertNull($resultItem->customFields); + $this->assertIsArray($resultItem->tags); + $this->assertCount(1, $resultItem->tags); + $this->assertEquals($params['tagsId'][0], $resultItem->tags[0]->id); + $this->assertEquals('www', $resultItem->tags[0]->name); + $this->assertIsArray($resultItem->users); + $this->assertCount(0, $resultItem->users); + $this->assertIsArray($resultItem->userGroups); + $this->assertCount(0, $resultItem->userGroups); + $this->assertNull($resultItem->customFields); + $this->assertIsArray($resultItem->links); + $this->assertEquals('self', $resultItem->links[0]->rel); + $this->assertNotEmpty($resultItem->links[0]->uri); + } + + /** + * @dataProvider getUnsetParams + * + * @throws \DI\DependencyException + * @throws \JsonException + * @throws \DI\NotFoundException + */ + public function testEditActionRequiredParameter(string $unsetParam): void + { + $response = $this->createAccount(); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id, + 'name' => 'API test edit', + 'categoryId' => 3, + 'clientId' => 3, + 'login' => 'admin', + 'expireDate' => time() + 86400, + 'url' => 'http://demo.syspass.org', + 'notes' => "test\n\ntest\nedit", + 'private' => 0, + 'privateGroup' => 0, + 'userId' => 1, + 'userGroupId' => 1, + 'parentId' => 1, + 'tagsId' => [1] + ]; + + unset($params[$unsetParam]); + + $api = $this->callApi( + ActionsInterface::ACCOUNT_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + } + + /** + * @throws \DI\DependencyException + * @throws \JsonException + * @throws \DI\NotFoundException + */ + public function testEditActionNonExistant(): void + { + $params = [ + 'id' => 10, + 'name' => 'API test edit', + 'categoryId' => 3, + 'clientId' => 3, + 'login' => 'admin', + 'expireDate' => time() + 86400, + 'url' => 'http://demo.syspass.org', + 'notes' => "test\n\ntest\nedit", + 'private' => 0, + 'privateGroup' => 0, + 'userId' => 1, + 'userGroupId' => 1, + 'parentId' => 1, + 'tagsId' => [1] + ]; + + $api = $this->callApi( + ActionsInterface::ACCOUNT_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('The account doesn\'t exist', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testDeleteAction(): void + { + $response = $this->createAccount(); + + $id = $response->result->itemId; + + $api = $this->callApi( + ActionsInterface::ACCOUNT_DELETE, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals('Account removed', $response->result->resultMessage); + $this->assertEquals($id, $response->result->itemId); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testDeleteActionNonExistant(): void + { + $api = $this->callApi( + ActionsInterface::ACCOUNT_DELETE, + ['id' => 10] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('The account doesn\'t exist', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testDeleteActionRequiredParameters(): void + { + $api = $this->callApi( + ActionsInterface::ACCOUNT_DELETE, + [] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + } + + public function searchProvider(): array + { + return [ + [ + [], + 2 + ], + [ + ['count' => 1], + 1 + ], + [ + ['text' => 'Google'], + 1 + ], + [ + ['text' => 'admin'], + 2 + ], + [ + ['text' => 'aaa'], + 1 + ], + [ + ['clientId' => 2], + 1 + ], + [ + ['clientId' => 3], + 0 + ], + [ + ['categoryId' => 1], + 1 + ], + [ + ['categoryId' => 2], + 1 + ], + [ + ['categoryId' => 10], + 0 + ], + [ + ['tagsId' => [3]], + 1 + ], + [ + ['tagsId' => [1, 3]], + 1 + ], + [ + [ + 'tagsId' => [1, 3], + 'op' => 'or' + ], + 2 + ], + [ + ['tagsId' => [1, 4]], + 0 + ], + [ + ['tagsId' => [10]], + 0 + ], + [ + [ + 'categoryId' => 1, + 'clientId' => 1 + ], + 1 + ], + [ + [ + 'categoryId' => 2, + 'clientId' => 1 + ], + 0 + ], + [ + [ + 'categoryId' => 2, + 'clientId' => 1, + 'op' => 'or' + ], + 2 + ], + ]; + } + + public function getUnsetParams(): array + { + return [ + ['name'], + ['clientId'], + ['categoryId'], + ]; } } diff --git a/tests/SP/Modules/Api/Controllers/CategoryControllerTest.php b/tests/SP/Modules/Api/Controllers/CategoryControllerTest.php index 2bd9c32d..1299c131 100644 --- a/tests/SP/Modules/Api/Controllers/CategoryControllerTest.php +++ b/tests/SP/Modules/Api/Controllers/CategoryControllerTest.php @@ -24,8 +24,8 @@ namespace SP\Tests\Modules\Api\Controllers; -use SP\Tests\Modules\Api\ApiTest; -use SP\Tests\WebTestCase; +use SP\Core\Acl\ActionsInterface; +use SP\Tests\Modules\Api\ApiTestCase; use stdClass; /** @@ -33,174 +33,364 @@ use stdClass; * * @package SP\Tests\Modules\Api\Controllers */ -class CategoryControllerTest extends WebTestCase +class CategoryControllerTest extends ApiTestCase { + private const PARAMS = [ + 'name' => 'API Category', + 'description' => "API test\ndescription" + ]; + /** - * @return int + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testCreateAction() + public function testCreateAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'category/create', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'name' => 'API Category', - 'description' => "API test\ndescription" - ], - 'id' => 1 - ]; + $response = $this->createCategory(self::PARAMS); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $this->assertEquals(0, $response->result->resultCode); + $this->assertNull($response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals(4, $response->result->itemId); + $this->assertEquals('Category added', $response->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals(5, $result->result->itemId); - $this->assertEquals('Category added', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); + $resultItem = $response->result->result; - return $result->result->itemId; + $this->assertEquals($response->result->itemId, $resultItem->id); + $this->assertEquals(self::PARAMS['name'], $resultItem->name); + $this->assertEquals(self::PARAMS['description'], $resultItem->description); } /** - * @depends testCreateAction + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + private function createCategory(?array $params = null): stdClass + { + $api = $this->callApi( + ActionsInterface::CATEGORY_CREATE, + $params ?? self::PARAMS + ); + + return self::processJsonResponse($api); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testCreateActionDuplicated(): void + { + $response = $this->createCategory(['name' => 'web']); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Duplicated category', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testCreateActionRequiredParameter(): void + { + $params = self::PARAMS; + unset($params['name']); + + $response = $this->createCategory($params); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testViewAction(): void + { + $response = $this->createCategory(self::PARAMS); + + $id = $response->result->itemId; + + $api = $this->callApi( + ActionsInterface::CATEGORY_VIEW, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result->data; + + $this->assertEquals(self::PARAMS['name'], $resultItem->name); + $this->assertEquals(self::PARAMS['description'], $resultItem->description); + $this->assertNull($resultItem->customFields); + $this->assertIsArray($resultItem->links); + $this->assertEquals('self', $resultItem->links[0]->rel); + $this->assertNotEmpty($resultItem->links[0]->uri); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testViewActionNonExistant(): void + { + $api = $this->callApi( + ActionsInterface::CATEGORY_VIEW, + ['id' => 10] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Category not found', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditAction(): void + { + $response = $this->createCategory(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id, + 'name' => 'API test edit', + 'description' => "API test\ndescription\nedit" + ]; + + $api = $this->callApi( + ActionsInterface::CATEGORY_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals('Category updated', $response->result->resultMessage); + $this->assertEquals($id, $response->result->itemId); + + $api = $this->callApi( + ActionsInterface::CATEGORY_VIEW, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result->data; + + $this->assertEquals($params['name'], $resultItem->name); + $this->assertEquals($params['description'], $resultItem->description); + $this->assertNull($resultItem->customFields); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionDuplicated(): void + { + $response = $this->createCategory(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id, + 'name' => 'web' + ]; + + $api = $this->callApi( + ActionsInterface::CATEGORY_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Duplicated category name', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionRequiredParameters(): void + { + $response = $this->createCategory(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id + ]; + + $api = $this->callApi( + ActionsInterface::CATEGORY_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionNonExistant(): void + { + $params = [ + 'id' => 10, + 'name' => 'API test edit', + 'description' => "API test\ndescription\nedit" + ]; + + $api = $this->callApi( + ActionsInterface::CATEGORY_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(0, $response->result->count); + } + + /** + * @dataProvider searchProvider * - * @param $id + * @throws \DI\DependencyException + * @throws \JsonException + * @throws \DI\NotFoundException */ - public function testViewAction($id) + public function testSearchActionByFilter(array $filter, int $resultsCount): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'category/view', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id - ], - 'id' => 1 - ]; + $api = $this->callApi( + ActionsInterface::CATEGORY_SEARCH, + $filter + ); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = self::processJsonResponse($api); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->result->id); - $this->assertEquals('API Category', $result->result->result->name); - $this->assertEquals("API test\ndescription", $result->result->result->description); + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals($resultsCount, $response->result->count); + $this->assertCount($resultsCount, $response->result->result); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testEditAction($id) + public function testDeleteAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'category/edit', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - 'name' => 'API category edit', - 'description' => "API test\ndescription\nedit" - ], - 'id' => 1 - ]; + $response = $this->createCategory(); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $id = $response->result->itemId; - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('Category updated', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); - } + $api = $this->callApi( + ActionsInterface::CATEGORY_DELETE, + ['id' => $id] + ); - public function testSearchAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'category/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN - ], - 'id' => 1 - ]; + $response = self::processJsonResponse($api); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(5, $result->result->count); - $this->assertCount(5, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'category/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'count' => 1 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - } - - public function testSearchByTextAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'category/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'text' => 'API category edit' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - $this->assertEquals('API category edit', $result->result->result[0]->name); - $this->assertEquals("API test\ndescription\nedit", $result->result->result[0]->description); + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals('Category deleted', $response->result->resultMessage); + $this->assertEquals($id, $response->result->itemId); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testDeleteAction($id) + public function testDeleteActionNonExistant(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'category/delete', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, + $api = $this->callApi( + ActionsInterface::CATEGORY_DELETE, + ['id' => 10] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Category not found', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testDeleteActionRequiredParameters(): void + { + $api = $this->callApi( + ActionsInterface::CATEGORY_DELETE, + [] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + public function searchProvider(): array + { + return [ + [ + [], + 3 ], - 'id' => 1 + [ + ['count' => 1], + 1 + ], + [ + ['text' => 'Linux'], + 1 + ], + [ + ['text' => 'Windows'], + 0 + ] ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('Category deleted', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); } } diff --git a/tests/SP/Modules/Api/Controllers/ClientControllerTest.php b/tests/SP/Modules/Api/Controllers/ClientControllerTest.php index f2c4e763..d6e5f5a6 100644 --- a/tests/SP/Modules/Api/Controllers/ClientControllerTest.php +++ b/tests/SP/Modules/Api/Controllers/ClientControllerTest.php @@ -24,8 +24,8 @@ namespace SP\Tests\Modules\Api\Controllers; -use SP\Tests\Modules\Api\ApiTest; -use SP\Tests\WebTestCase; +use SP\Core\Acl\ActionsInterface; +use SP\Tests\Modules\Api\ApiTestCase; use stdClass; /** @@ -33,178 +33,374 @@ use stdClass; * * @package SP\Tests\Modules\Api\Controllers */ -class ClientControllerTest extends WebTestCase +class ClientControllerTest extends ApiTestCase { + private const PARAMS = [ + 'name' => 'API Client', + 'description' => "API test\ndescription", + 'global' => 1 + ]; + /** - * @return int + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testCreateAction() + public function testCreateAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'client/create', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'name' => 'API Client', - 'description' => "API test\ndescription", - 'global' => 1 - ], - 'id' => 1 - ]; + $response = $this->createClient(self::PARAMS); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $this->assertEquals(0, $response->result->resultCode); + $this->assertNull($response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals(5, $response->result->itemId); + $this->assertEquals('Client added', $response->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals(4, $result->result->itemId); - $this->assertEquals('Client added', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); + $resultItem = $response->result->result; - return $result->result->itemId; + $this->assertEquals($response->result->itemId, $resultItem->id); + $this->assertEquals(self::PARAMS['name'], $resultItem->name); + $this->assertEquals(self::PARAMS['description'], $resultItem->description); + $this->assertEquals(self::PARAMS['global'], $resultItem->isGlobal); } /** - * @depends testCreateAction + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + private function createClient(?array $params = null): stdClass + { + $api = $this->callApi( + ActionsInterface::CLIENT_CREATE, + $params ?? self::PARAMS + ); + + return self::processJsonResponse($api); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testCreateActionDuplicated(): void + { + $response = $this->createClient(['name' => 'Google']); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Duplicated client', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testCreateActionRequiredParameters(): void + { + $params = self::PARAMS; + unset($params['name']); + + $response = $this->createClient($params); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testViewAction(): void + { + $response = $this->createClient(self::PARAMS); + + $id = $response->result->itemId; + + $api = $this->callApi( + ActionsInterface::CLIENT_VIEW, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result->data; + + $this->assertEquals(self::PARAMS['name'], $resultItem->name); + $this->assertEquals(self::PARAMS['description'], $resultItem->description); + $this->assertEquals(self::PARAMS['global'], $resultItem->isGlobal); + $this->assertNull($resultItem->customFields); + $this->assertIsArray($resultItem->links); + $this->assertEquals('self', $resultItem->links[0]->rel); + $this->assertNotEmpty($resultItem->links[0]->uri); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testViewActionNonExistant(): void + { + $api = $this->callApi( + ActionsInterface::CLIENT_VIEW, + ['id' => 10] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Client not found', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditAction(): void + { + $response = $this->createClient(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id, + 'name' => 'API Client edit', + 'description' => "API test\ndescription\nedit", + 'global' => 0 + ]; + + $api = $this->callApi( + ActionsInterface::CLIENT_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals('Client updated', $response->result->resultMessage); + $this->assertEquals($id, $response->result->itemId); + + $api = $this->callApi( + ActionsInterface::CLIENT_VIEW, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result->data; + + $this->assertEquals($params['name'], $resultItem->name); + $this->assertEquals($params['description'], $resultItem->description); + $this->assertEquals($params['global'], $resultItem->isGlobal); + $this->assertNull($resultItem->customFields); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionDuplicated(): void + { + $response = $this->createClient(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id, + 'name' => 'Google' + ]; + + $api = $this->callApi( + ActionsInterface::CLIENT_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Duplicated client', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionRequiredParameters(): void + { + $response = $this->createClient(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id + ]; + + $api = $this->callApi( + ActionsInterface::CLIENT_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionNonExistant(): void + { + $params = [ + 'id' => 10, + 'name' => 'API Client edit', + 'description' => "API test\ndescription\nedit", + 'global' => 0 + ]; + + $api = $this->callApi( + ActionsInterface::CLIENT_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(0, $response->result->count); + } + + /** + * @dataProvider searchProvider * - * @param $id + * @throws \DI\DependencyException + * @throws \JsonException + * @throws \DI\NotFoundException */ - public function testViewAction($id) + public function testSearchActionByFilter(array $filter, int $resultsCount): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'client/view', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id - ], - 'id' => 1 - ]; + $api = $this->callApi( + ActionsInterface::CLIENT_SEARCH, + $filter + ); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = self::processJsonResponse($api); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->result->id); - $this->assertEquals('API Client', $result->result->result->name); - $this->assertEquals("API test\ndescription", $result->result->result->description); - $this->assertEquals(1, $result->result->result->isGlobal); + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals($resultsCount, $response->result->count); + $this->assertCount($resultsCount, $response->result->result); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testEditAction($id) + public function testDeleteAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'client/edit', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - 'name' => 'API Client edit', - 'description' => "API test\ndescription\nedit", - 'global' => 0 - ], - 'id' => 1 - ]; + $response = $this->createClient(); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $id = $response->result->itemId; - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('Client updated', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); - } + $api = $this->callApi( + ActionsInterface::CLIENT_DELETE, + ['id' => $id] + ); - public function testSearchAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'client/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN - ], - 'id' => 1 - ]; + $response = self::processJsonResponse($api); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(4, $result->result->count); - $this->assertCount(4, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'client/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'count' => 1 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - } - - public function testSearchByTextAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'client/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'text' => 'API Client edit' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - $this->assertEquals('API Client edit', $result->result->result[0]->name); - $this->assertEquals("API test\ndescription\nedit", $result->result->result[0]->description); - $this->assertEquals(0, $result->result->result[0]->isGlobal); + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals('Client deleted', $response->result->resultMessage); + $this->assertEquals($id, $response->result->itemId); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testDeleteAction($id) + public function testDeleteActionNonExistant(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'client/delete', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, + $api = $this->callApi( + ActionsInterface::CLIENT_DELETE, + ['id' => 10] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Client not found', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testDeleteActionRequiredParameters(): void + { + $api = $this->callApi( + ActionsInterface::CLIENT_DELETE, + [] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + public function searchProvider(): array + { + return [ + [ + [], + 4 ], - 'id' => 1 + [ + ['count' => 1], + 1 + ], + [ + ['text' => 'Google'], + 1 + ], + [ + ['text' => 'Inc'], + 3 + ], + [ + ['text' => 'Spotify'], + 0 + ] ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('Client deleted', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); } } diff --git a/tests/SP/Modules/Api/Controllers/ConfigControllerTest.php b/tests/SP/Modules/Api/Controllers/ConfigControllerTest.php index 769824a5..b6a60390 100644 --- a/tests/SP/Modules/Api/Controllers/ConfigControllerTest.php +++ b/tests/SP/Modules/Api/Controllers/ConfigControllerTest.php @@ -24,8 +24,8 @@ namespace SP\Tests\Modules\Api\Controllers; -use SP\Tests\Modules\Api\ApiTest; -use SP\Tests\WebTestCase; +use SP\Core\Acl\ActionsInterface; +use SP\Tests\Modules\Api\ApiTestCase; use stdClass; /** @@ -33,48 +33,141 @@ use stdClass; * * @package SP\Tests\Modules\Api\Controllers */ -class ConfigControllerTest extends WebTestCase +class ConfigControllerTest extends ApiTestCase { - public function testExportAction() + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testExportAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'config/export', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN - ], - 'id' => 1 - ]; + $api = $this->callApi( + ActionsInterface::CONFIG_EXPORT_RUN, + [] + ); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = self::processJsonResponse($api); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals('/var/www/html/sysPass/app/backup', $result->result->result); - $this->assertEquals('Export process finished', $result->result->resultMessage); - $this->assertEquals(0, $result->result->resultCode); + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertEquals('Export process finished', $response->result->resultMessage); + $this->assertEquals(0, $response->result->resultCode); + $this->assertNotEmpty($response->result->result->files->xml); + $this->assertFileExists($response->result->result->files->xml); } - public function testBackupAction() + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testExportActionCustomPath(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'config/backup', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN - ], - 'id' => 1 - ]; + $api = $this->callApi( + ActionsInterface::CONFIG_EXPORT_RUN, + [ + 'path' => TMP_PATH . '/export/custom/path' + ] + ); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = self::processJsonResponse($api); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals(0, $result->result->itemId); - $this->assertEquals('/var/www/html/sysPass/app/backup', $result->result->result); - $this->assertEquals('Backup process finished', $result->result->resultMessage); - $this->assertEquals(0, $result->result->resultCode); + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertEquals('Export process finished', $response->result->resultMessage); + $this->assertEquals(0, $response->result->resultCode); + $this->assertNotEmpty($response->result->result->files->xml); + $this->assertFileExists($response->result->result->files->xml); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testExportActionInvalidPath(): void + { + $api = $this->callApi( + ActionsInterface::CONFIG_EXPORT_RUN, + [ + 'path' => '/export/custom/path' + ] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertStringContainsString('Unable to create the directory', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testBackupAction(): void + { + $api = $this->callApi( + ActionsInterface::CONFIG_BACKUP_RUN, + [] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertEquals('Backup process finished', $response->result->resultMessage); + $this->assertEquals(0, $response->result->resultCode); + $this->assertNotEmpty($response->result->result->files->app); + $this->assertNotEmpty($response->result->result->files->db); + $this->assertFileExists($response->result->result->files->app); + $this->assertFileExists($response->result->result->files->db); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testBackupActionInvalidPath(): void + { + $api = $this->callApi( + ActionsInterface::CONFIG_BACKUP_RUN, + [ + 'path' => '/backup/custom/path' + ] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertStringContainsString('Unable to create the backups directory', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testBackupActionCustomPath(): void + { + $api = $this->callApi( + ActionsInterface::CONFIG_BACKUP_RUN, + [ + 'path' => TMP_PATH . '/backup/custom/path' + ] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(1, $response->result->count); + $this->assertEquals('Backup process finished', $response->result->resultMessage); + $this->assertEquals(0, $response->result->resultCode); + $this->assertNotEmpty($response->result->result->files->app); + $this->assertNotEmpty($response->result->result->files->db); + $this->assertFileExists($response->result->result->files->app); + $this->assertFileExists($response->result->result->files->db); } } diff --git a/tests/SP/Modules/Api/Controllers/TagControllerTest.php b/tests/SP/Modules/Api/Controllers/TagControllerTest.php index 8b8ef2f9..b3a92a59 100644 --- a/tests/SP/Modules/Api/Controllers/TagControllerTest.php +++ b/tests/SP/Modules/Api/Controllers/TagControllerTest.php @@ -24,8 +24,8 @@ namespace SP\Tests\Modules\Api\Controllers; -use SP\Tests\Modules\Api\ApiTest; -use SP\Tests\WebTestCase; +use SP\Core\Acl\ActionsInterface; +use SP\Tests\Modules\Api\ApiTestCase; use stdClass; /** @@ -33,171 +33,348 @@ use stdClass; * * @package SP\Tests\Modules\Api\Controllers */ -class TagControllerTest extends WebTestCase +class TagControllerTest extends ApiTestCase { + private const PARAMS = [ + 'name' => 'API Tag' + ]; + /** - * @return int + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testCreateAction() + public function testCreateAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'tag/create', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'name' => 'API Tag', - 'description' => "API test\ndescription" - ], - 'id' => 1 - ]; + $response = $this->createTag(self::PARAMS); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $this->assertEquals(0, $response->result->resultCode); + $this->assertNull($response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals(4, $response->result->itemId); + $this->assertEquals('Tag added', $response->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals(7, $result->result->itemId); - $this->assertEquals('Tag added', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); + $resultItem = $response->result->result; - return $result->result->itemId; + $this->assertEquals($response->result->itemId, $resultItem->id); + $this->assertEquals(self::PARAMS['name'], $resultItem->name); } /** - * @depends testCreateAction + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + private function createTag(?array $params = null): stdClass + { + $api = $this->callApi( + ActionsInterface::TAG_CREATE, + $params ?? self::PARAMS + ); + + return self::processJsonResponse($api); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testCreateActionDuplicated(): void + { + $response = $this->createTag(['name' => 'linux']); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Duplicated tag', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testCreateActionRequiredParameters(): void + { + $response = $this->createTag([]); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testViewAction(): void + { + $response = $this->createTag(self::PARAMS); + + $id = $response->result->itemId; + + $api = $this->callApi( + ActionsInterface::TAG_VIEW, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result; + + $this->assertEquals(self::PARAMS['name'], $resultItem->name); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testViewActionNonExistant(): void + { + $api = $this->callApi( + ActionsInterface::TAG_VIEW, + ['id' => 10] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Tag not found', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditAction(): void + { + $response = $this->createTag(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id, + 'name' => 'API test edit' + ]; + + $api = $this->callApi( + ActionsInterface::TAG_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals('Tag updated', $response->result->resultMessage); + $this->assertEquals($id, $response->result->itemId); + + $api = $this->callApi( + ActionsInterface::TAG_VIEW, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result; + + $this->assertEquals($params['name'], $resultItem->name); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionDuplicated(): void + { + $response = $this->createTag(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id, + 'name' => 'linux' + ]; + + $api = $this->callApi( + ActionsInterface::TAG_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Duplicated tag', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionWrongParameters(): void + { + $response = $this->createTag(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id + ]; + + $api = $this->callApi( + ActionsInterface::TAG_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionNonExistant(): void + { + $params = [ + 'id' => 10, + 'name' => 'API test edit' + ]; + + $api = $this->callApi( + ActionsInterface::TAG_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(0, $response->result->count); + } + + /** + * @dataProvider searchProvider * - * @param $id + * @throws \DI\DependencyException + * @throws \JsonException + * @throws \DI\NotFoundException */ - public function testViewAction($id) + public function testSearchActionByFilter(array $filter, int $resultsCount): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'tag/view', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id - ], - 'id' => 1 - ]; + $api = $this->callApi( + ActionsInterface::TAG_SEARCH, + $filter + ); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = self::processJsonResponse($api); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->result->id); - $this->assertEquals('API Tag', $result->result->result->name); + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals($resultsCount, $response->result->count); + $this->assertCount($resultsCount, $response->result->result); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testEditAction($id) + public function testDeleteAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'tag/edit', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - 'name' => 'API Tag edit' - ], - 'id' => 1 - ]; + $response = $this->createTag(); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $id = $response->result->itemId; - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('Tag updated', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); - } + $api = $this->callApi( + ActionsInterface::TAG_DELETE, + ['id' => $id] + ); - public function testSearchAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'tag/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN - ], - 'id' => 1 - ]; + $response = self::processJsonResponse($api); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(7, $result->result->count); - $this->assertCount(7, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'tag/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'count' => 1 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - } - - public function testSearchByTextAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'tag/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'text' => 'API Tag edit' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - $this->assertEquals('API Tag edit', $result->result->result[0]->name); + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals('Tag removed', $response->result->resultMessage); + $this->assertEquals($id, $response->result->itemId); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testDeleteAction($id) + public function testDeleteActionNonExistant(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'tag/delete', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, + $api = $this->callApi( + ActionsInterface::TAG_DELETE, + ['id' => 10] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Tag not found', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testDeleteActionRequiredParameters(): void + { + $api = $this->callApi( + ActionsInterface::TAG_DELETE, + [] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + public function searchProvider(): array + { + return [ + [ + [], + 3 ], - 'id' => 1 + [ + ['count' => 1], + 1 + ], + [ + ['text' => 'Linux'], + 1 + ], + [ + ['text' => 'Google'], + 0 + ] ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('Tag removed', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); } } diff --git a/tests/SP/Modules/Api/Controllers/UserGroupControllerTest.php b/tests/SP/Modules/Api/Controllers/UserGroupControllerTest.php index bf52ad2d..e707f130 100644 --- a/tests/SP/Modules/Api/Controllers/UserGroupControllerTest.php +++ b/tests/SP/Modules/Api/Controllers/UserGroupControllerTest.php @@ -24,8 +24,8 @@ namespace SP\Tests\Modules\Api\Controllers; -use SP\Tests\Modules\Api\ApiTest; -use SP\Tests\WebTestCase; +use SP\Core\Acl\ActionsInterface; +use SP\Tests\Modules\Api\ApiTestCase; use stdClass; /** @@ -33,236 +33,407 @@ use stdClass; * * @package SP\Tests\Modules\Api\Controllers */ -class UserGroupControllerTest extends WebTestCase +class UserGroupControllerTest extends ApiTestCase { + private const PARAMS = [ + 'name' => 'API UserGroup', + 'description' => "API test\ndescription", + 'usersId' => [3, 4] + ]; + /** - * @return int + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testCreateAction() + public function testCreateAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'userGroup/create', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'name' => 'API UserGroup', - 'description' => "API test\ndescription", - 'usersId' => [1] - ], - 'id' => 1 - ]; + $response = $this->createUserGroup(self::PARAMS); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $this->assertEquals(0, $response->result->resultCode); + $this->assertNull($response->result->count); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals(7, $response->result->itemId); + $this->assertEquals('Group added', $response->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals(2, $result->result->itemId); - $this->assertEquals('Group added', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); + $resultItem = $response->result->result; - return $result->result->itemId; + $this->assertEquals($response->result->itemId, $resultItem->id); + $this->assertEquals(self::PARAMS['name'], $resultItem->name); + $this->assertEquals(self::PARAMS['description'], $resultItem->description); + $this->assertCount(2, $resultItem->users); + $this->assertEquals(self::PARAMS['usersId'][0], $resultItem->users[0]); } /** - * @depends testCreateAction - * - * @param $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testViewAction($id) + private function createUserGroup(?array $params = null): stdClass { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'userGroup/view', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id - ], - 'id' => 1 - ]; + $api = $this->callApi( + ActionsInterface::GROUP_CREATE, + $params ?? self::PARAMS + ); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->result->id); - $this->assertEquals('API UserGroup', $result->result->result->name); - $this->assertEquals("API test\ndescription", $result->result->result->description); - $this->assertArrayHasKey(0, $result->result->result->users); - $this->assertEquals(1, $result->result->result->users[0]); + return self::processJsonResponse($api); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testEditAction($id) + public function testCreateActionInvalidUser(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'userGroup/edit', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - 'name' => 'API UserGroup edit', - ], - 'id' => 1 - ]; + $params = self::PARAMS; + $params['usersId'] = [10]; - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = $this->createUserGroup($params); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('Group updated', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Integrity constraint', $response->error->message); } /** - * @depends testCreateAction - * - * @param int $id - * - * @return int + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testEditActionNoUsers($id) + public function testCreateActionRequiredParameters(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'userGroup/edit', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - 'name' => 'API UserGroup edit', - 'usersId' => [] - ], - 'id' => 1 - ]; + $params = self::PARAMS; + unset($params['name']); - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = $this->createUserGroup($params); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('Group updated', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); - - return $id; + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); } /** - * @depends testEditActionNoUsers - * - * @param $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testViewActionNoUsers($id) + public function testCreateActionDuplicatedName(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'userGroup/view', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id - ], - 'id' => 1 - ]; + $params = self::PARAMS; + $params['name'] = 'Admins'; - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $response = $this->createUserGroup($params); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->result->id); - $this->assertEquals('API UserGroup edit', $result->result->result->name); - $this->assertEmpty($result->result->result->description); - $this->assertCount(0, $result->result->result->users); - } - - public function testSearchAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'userGroup/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(2, $result->result->count); - $this->assertCount(2, $result->result->result); - - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'userGroup/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'count' => 1 - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - } - - public function testSearchByTextAction() - { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'userGroup/search', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'text' => 'API UserGroup edit' - ], - 'id' => 1 - ]; - - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); - - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertEquals(1, $result->result->count); - $this->assertCount(1, $result->result->result); - $this->assertEquals('API UserGroup edit', $result->result->result[0]->name); + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Duplicated group name', $response->error->message); } /** - * @depends testCreateAction - * - * @param int $id + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException */ - public function testDeleteAction($id) + public function testViewAction(): void { - $data = [ - 'jsonrpc' => '2.0', - 'method' => 'userGroup/delete', - 'params' => [ - 'authToken' => ApiTest::API_TOKEN, - 'id' => $id, - ], - 'id' => 1 + $response = $this->createUserGroup(self::PARAMS); + + $id = $response->result->itemId; + + $api = $this->callApi( + ActionsInterface::GROUP_VIEW, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result; + + $this->assertEquals(self::PARAMS['name'], $resultItem->name); + $this->assertEquals(self::PARAMS['description'], $resultItem->description); + $this->assertCount(2, $resultItem->users); + $this->assertEquals(self::PARAMS['usersId'][0], $resultItem->users[0]); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testViewActionNonExistant(): void + { + $api = $this->callApi( + ActionsInterface::GROUP_VIEW, + ['id' => 10] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Group not found', $response->error->message); + } + + /** + * @dataProvider getGroupUsers + * + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditAction(array $users, int $usersCount): void + { + $response = $this->createUserGroup(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id, + 'name' => 'API test edit', + 'description' => "API test\ndescription", + 'usersId' => $users ]; - $result = self::checkAndProcessJsonResponse(self::postJson(ApiTest::API_URL, $data)); + $api = $this->callApi( + ActionsInterface::GROUP_EDIT, + $params + ); - $this->assertInstanceOf(stdClass::class, $result); - $this->assertEquals(0, $result->result->resultCode); - $this->assertNull($result->result->count); - $this->assertEquals($id, $result->result->itemId); - $this->assertEquals('Group deleted', $result->result->resultMessage); - $this->assertInstanceOf(stdClass::class, $result->result->result); + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals('Group updated', $response->result->resultMessage); + $this->assertEquals($id, $response->result->itemId); + + $api = $this->callApi( + ActionsInterface::GROUP_VIEW, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals($id, $response->result->itemId); + + $resultItem = $response->result->result; + + $this->assertEquals($params['name'], $resultItem->name); + $this->assertEquals($params['description'], $resultItem->description); + $this->assertCount($usersCount, $resultItem->users); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionInvalidUser(): void + { + $response = $this->createUserGroup(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id, + 'name' => 'API test edit', + 'description' => "API test\ndescription", + 'usersId' => [10] + ]; + + $api = $this->callApi( + ActionsInterface::GROUP_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Integrity constraint', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionRequiredParameters(): void + { + $response = $this->createUserGroup(self::PARAMS); + + $id = $response->result->itemId; + + $params = [ + 'id' => $id + ]; + + $api = $this->callApi( + ActionsInterface::GROUP_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testEditActionNonExistant(): void + { + $params = [ + 'id' => 10, + 'name' => 'API test edit' + ]; + + $api = $this->callApi( + ActionsInterface::GROUP_EDIT, + $params + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals(0, $response->result->count); + } + + /** + * @dataProvider searchProvider + * + * @throws \DI\DependencyException + * @throws \JsonException + * @throws \DI\NotFoundException + */ + public function testSearchActionByFilter(array $filter, int $resultsCount): void + { + $api = $this->callApi( + ActionsInterface::GROUP_SEARCH, + $filter + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertEquals($resultsCount, $response->result->count); + $this->assertCount($resultsCount, $response->result->result); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testDeleteAction(): void + { + $response = $this->createUserGroup(); + + $id = $response->result->itemId; + + $api = $this->callApi( + ActionsInterface::GROUP_DELETE, + ['id' => $id] + ); + + $response = self::processJsonResponse($api); + + $this->assertEquals(0, $response->result->resultCode); + $this->assertInstanceOf(stdClass::class, $response->result); + $this->assertEquals('Group deleted', $response->result->resultMessage); + $this->assertEquals($id, $response->result->itemId); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testDeleteActionNonExistant(): void + { + $api = $this->callApi( + ActionsInterface::GROUP_DELETE, + ['id' => 10] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Group not found', $response->error->message); + } + + /** + * @throws \DI\DependencyException + * @throws \DI\NotFoundException + * @throws \JsonException + */ + public function testDeleteActionRequiredParameters(): void + { + $api = $this->callApi( + ActionsInterface::GROUP_DELETE, + [] + ); + + $response = self::processJsonResponse($api); + + $this->assertInstanceOf(stdClass::class, $response->error); + $this->assertEquals('Wrong parameters', $response->error->message); + $this->assertInstanceOf(stdClass::class, $response->error->data); + $this->assertIsArray($response->error->data->help); + + } + + public function searchProvider(): array + { + return [ + [ + [], + 6 + ], + [ + ['count' => 1], + 1 + ], + [ + ['text' => 'Demo'], + 1 + ], + [ + ['text' => 'Test'], + 3 + ], + [ + ['text' => 'Grupo'], + 1 + ] + ]; + } + + public function getGroupUsers(): array + { + return [ + [ + [2, 3, 4], + 3 + ], + [ + [2, 3], + 2 + ], + [ + [], + 0 + ], + ]; } } diff --git a/tests/SP/Modules/Cli/CliTestCase.php b/tests/SP/Modules/Cli/CliTestCase.php index ba3cb61a..0ba9a6dd 100644 --- a/tests/SP/Modules/Cli/CliTestCase.php +++ b/tests/SP/Modules/Cli/CliTestCase.php @@ -34,6 +34,9 @@ use SP\Core\Context\ContextInterface; use SP\Storage\Database\DBStorageInterface; use Symfony\Component\Console\Tester\CommandTester; use function SP\Tests\getDbHandler; +use const SP\Tests\APP_DEFINITIONS_FILE; + +define('APP_MODULE', 'cli'); /** * Class CliTestCase @@ -57,8 +60,8 @@ abstract class CliTestCase extends TestCase $builder = new ContainerBuilder(); $builder->addDefinitions( - APP_ROOT . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'Definitions.php', - MODULES_PATH . DIRECTORY_SEPARATOR . 'cli' . DIRECTORY_SEPARATOR . 'definitions.php' + APP_DEFINITIONS_FILE, + MODULES_PATH . DIRECTORY_SEPARATOR . 'cli' . DIRECTORY_SEPARATOR . 'module.php' ); self::$dic = $builder->build(); diff --git a/tests/SP/Storage/FileCachePackedTest.php b/tests/SP/Storage/FileCachePackedTest.php index 16e08561..dd2110af 100644 --- a/tests/SP/Storage/FileCachePackedTest.php +++ b/tests/SP/Storage/FileCachePackedTest.php @@ -72,7 +72,8 @@ class FileCachePackedTest extends TestCase */ public function testDeleteInvalid() { - $this->expectException(FileException::class); + $this->expectNotToPerformAssertions(); + $cache = new FileCachePacked(self::CACHE_FILE); $cache->delete(); } diff --git a/tests/SP/Storage/FileCacheTest.php b/tests/SP/Storage/FileCacheTest.php index 0f1aff1e..202de27f 100644 --- a/tests/SP/Storage/FileCacheTest.php +++ b/tests/SP/Storage/FileCacheTest.php @@ -72,7 +72,8 @@ class FileCacheTest extends TestCase */ public function testDeleteInvalid() { - $this->expectException(FileException::class); + $this->expectNotToPerformAssertions(); + $cache = new FileCache(self::CACHE_FILE); $cache->delete(); } diff --git a/tests/SP/bootstrap.php b/tests/SP/bootstrap.php index 78cc1626..34508a9a 100644 --- a/tests/SP/bootstrap.php +++ b/tests/SP/bootstrap.php @@ -39,24 +39,24 @@ use SP\Storage\Database\MySQLHandler; use SP\Util\FileUtil; define('DEBUG', true); - -define('APP_MODULE', 'tests'); - +define('IS_TESTING', true); define('APP_ROOT', dirname(__DIR__, 2)); define('TEST_ROOT', dirname(__DIR__)); + +const APP_DEFINITIONS_FILE = APP_ROOT . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'Definitions.php'; + define('RESOURCE_PATH', TEST_ROOT . DIRECTORY_SEPARATOR . 'res'); define('CONFIG_PATH', RESOURCE_PATH . DIRECTORY_SEPARATOR . 'config'); define('CONFIG_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . 'config.xml'); define('ACTIONS_FILE', CONFIG_PATH . DIRECTORY_SEPARATOR . 'actions.xml'); - +define('LOCALES_PATH', APP_ROOT . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'locales'); define('MODULES_PATH', APP_ROOT . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'modules'); define('SQL_PATH', APP_ROOT . DIRECTORY_SEPARATOR . 'schemas'); define('CACHE_PATH', RESOURCE_PATH . DIRECTORY_SEPARATOR . 'cache'); define('TMP_PATH', TEST_ROOT . DIRECTORY_SEPARATOR . 'tmp'); define('BACKUP_PATH', TMP_PATH); - +define('PLUGINS_PATH', TMP_PATH); define('XML_SCHEMA', APP_ROOT . DIRECTORY_SEPARATOR . 'schemas' . DIRECTORY_SEPARATOR . 'syspass.xsd'); - define('LOG_FILE', TMP_PATH . DIRECTORY_SEPARATOR . 'test.log'); define('FIXTURE_FILES', [ RESOURCE_PATH . DIRECTORY_SEPARATOR . 'datasets' . DIRECTORY_SEPARATOR . 'truncate.sql', @@ -68,9 +68,9 @@ define('SELF_HOSTNAME', gethostbyaddr(SELF_IP_ADDRESS)); require_once APP_ROOT . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; require_once APP_ROOT . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'BaseFunctions.php'; -print 'APP_ROOT=' . APP_ROOT . PHP_EOL; -print 'TEST_ROOT=' . TEST_ROOT . PHP_EOL; -print 'SELF_IP_ADDRESS=' . SELF_IP_ADDRESS . PHP_EOL; +logger('APP_ROOT=' . APP_ROOT); +logger('TEST_ROOT=' . TEST_ROOT); +logger('SELF_IP_ADDRESS=' . SELF_IP_ADDRESS); // Setup directories recreateDir(TMP_PATH); @@ -88,22 +88,13 @@ if (is_dir(CONFIG_PATH) * Función para llamadas a gettext */ if (!function_exists('\gettext')) { - /** - * - * @param $str - * - * @return string - */ - function gettext($str): string + function gettext(string $str): string { return $str; } } -/** - * @return string - */ -function getRealIpAddress() +function getRealIpAddress(): string { return trim(shell_exec('ip a s eth0 | awk \'$1 == "inet" {print $2}\' | cut -d"/" -f1')) ?: '127.0.0.1'; } @@ -119,10 +110,9 @@ function setupContext(): Container { // Instancia del contenedor de dependencias con las definiciones de los objetos necesarios // para la aplicación - $builder = new ContainerBuilder(); -// $builder->setDefinitionCache(new ArrayCache()); - $builder->addDefinitions(APP_ROOT . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'Definitions.php'); - $dic = $builder->build(); + $dic = (new ContainerBuilder()) + ->addDefinitions(APP_DEFINITIONS_FILE) + ->build(); // Inicializar el contexto $context = $dic->get(ContextInterface::class); @@ -148,62 +138,38 @@ function setupContext(): Container return $dic; } -/** - * @param DatabaseConnectionData|null $connectionData - * - * @return MySQLHandler - */ -function getDbHandler(DatabaseConnectionData $connectionData = null): MySQLHandler +function getDbHandler(?DatabaseConnectionData $connectionData = null): MySQLHandler { if ($connectionData === null) { // Establecer configuración de conexión con la BBDD - $connectionData = (new DatabaseConnectionData()) - ->setDbHost(getenv('DB_SERVER')) - ->setDbName(getenv('DB_NAME')) - ->setDbUser(getenv('DB_USER')) - ->setDbPass(getenv('DB_PASS')); + $connectionData = DatabaseConnectionData::getFromEnvironment(); } return new MySQLHandler($connectionData); } -/** - * @param $dir - * @param $file - * - * @return string - */ -function getResource($dir, $file): string +function getResource(string $dir, string $file): string { return file_get_contents(RESOURCE_PATH . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . $file) ?: ''; } -/** - * @param $dir - * @param $file - * @param $data - * - * @return string - */ -function saveResource($dir, $file, $data): string +function saveResource(string $dir, string $file, string $data): string { return file_put_contents(RESOURCE_PATH . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . $file, $data); } /** - * @param $dir - * * @throws FileNotFoundException */ -function recreateDir($dir) +function recreateDir(string $dir) { if (is_dir($dir)) { - print 'Deleting ' . $dir . PHP_EOL; + logger('Deleting ' . $dir); FileUtil::rmdir_recursive($dir); } - print 'Creating ' . $dir . PHP_EOL; + logger('Creating ' . $dir . PHP_EOL); if (!mkdir($dir) && !is_dir($dir)) { throw new RuntimeException(sprintf('Directory "%s" was not created', $dir)); diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 083df282..5a832d1f 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -1,5 +1,6 @@ - + stopOnFailure="false" + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"> + + + ../lib/SP + + + ../lib/SP/DataModel + ../lib/SP/Providers + ../lib/SP/Html/Assets + ../lib/SP/Html/DataGrid + ../lib/SP/Config/ConfigData.php + + + + + + ./SP @@ -20,20 +39,5 @@ ./SP/Modules/Cli - - - ../lib/SP - - ../lib/SP/DataModel - ../lib/SP/Providers - ../lib/SP/Html/Assets - ../lib/SP/Html/DataGrid - ../lib/SP/Config/ConfigData.php - - - - - - - - \ No newline at end of file + +