diff options
author | i00171 <anton.delitsch@implema.se> | 2016-06-26 18:35:25 +0200 |
---|---|---|
committer | i00171 <anton.delitsch@implema.se> | 2016-06-26 18:35:25 +0200 |
commit | 47039d32c84ba699867920d2c3cb47a34b199b9d (patch) | |
tree | 4fbc2ec34889baeab00085e0509055dca7daee6a | |
parent | 911be6ed00c1ece5d9ef2c16e80899bb7bffad67 (diff) | |
parent | c110dffefe259c13e60193fb81ebb9d4b79504de (diff) |
Merge branch 'master' of https://github.com/fguillot/kanboard
111 files changed, 3251 insertions, 2634 deletions
diff --git a/.docker/crontab/kanboard b/.docker/crontab/cronjob.alpine index 91ad044e..91ad044e 100644 --- a/.docker/crontab/kanboard +++ b/.docker/crontab/cronjob.alpine diff --git a/.docker/crontab/cronjob.debian b/.docker/crontab/cronjob.debian new file mode 100644 index 00000000..40310d4f --- /dev/null +++ b/.docker/crontab/cronjob.debian @@ -0,0 +1 @@ +@daily www-data cd /var/www/html/kanboard && ./kanboard cronjob >/dev/null 2>&1 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..569d36ca --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +.git* +data/* +Makefile +.*.yml +*.json
\ No newline at end of file @@ -1,11 +1,18 @@ Version 1.0.31 (unreleased) -------------- +New features: + +* Added application and project roles validation for API procedure calls +* Added new API call: "getProjectByIdentifier" + Improvements: +* Added argument owner_id and identifier to project API calls +* Rewrite integration tests to run with Docker containers * Use the same task form layout everywhere -* Remove some tasks dropdown menus that are now available with task edit form -* Make embedded documentation available in multiple languages +* Removed some tasks dropdown menus that are now available with task edit form +* Make embedded documentation readable in multiple languages (if a translation is available) Bug fixes: @@ -24,8 +24,7 @@ COPY .docker/php/conf.d/local.ini /etc/php5/conf.d/ COPY .docker/php/php-fpm.conf /etc/php5/ COPY .docker/nginx/nginx.conf /etc/nginx/ COPY .docker/kanboard/config.php /var/www/kanboard/ -COPY .docker/kanboard/config.php /var/www/kanboard/ -COPY .docker/crontab/kanboard /var/spool/cron/crontabs/nginx +COPY .docker/crontab/cronjob.alpine /var/spool/cron/crontabs/nginx EXPOSE 80 @@ -58,7 +58,28 @@ test-postgres: unittest: test-sqlite test-mysql test-postgres test-browser: - @ phpunit -c tests/acceptance.xml + @ phpunit -c tests/acceptance.xml + +integration-test-mysql: + @ composer install + @ docker-compose -f tests/docker/compose.integration.mysql.yaml build + @ docker-compose -f tests/docker/compose.integration.mysql.yaml up -d mysql app + @ docker-compose -f tests/docker/compose.integration.mysql.yaml up tests + @ docker-compose -f tests/docker/compose.integration.mysql.yaml down + +integration-test-postgres: + @ composer install + @ docker-compose -f tests/docker/compose.integration.postgres.yaml build + @ docker-compose -f tests/docker/compose.integration.postgres.yaml up -d postgres app + @ docker-compose -f tests/docker/compose.integration.postgres.yaml up tests + @ docker-compose -f tests/docker/compose.integration.postgres.yaml down + +integration-test-sqlite: + @ composer install + @ docker-compose -f tests/docker/compose.integration.sqlite.yaml build + @ docker-compose -f tests/docker/compose.integration.sqlite.yaml up -d app + @ docker-compose -f tests/docker/compose.integration.sqlite.yaml up tests + @ docker-compose -f tests/docker/compose.integration.sqlite.yaml down sql: @ pg_dump --schema-only --no-owner --no-privileges --quote-all-identifiers -n public --file app/Schema/Sql/postgres.sql kanboard diff --git a/app/Api/Authorization/ActionAuthorization.php b/app/Api/Authorization/ActionAuthorization.php new file mode 100644 index 00000000..4b41ad82 --- /dev/null +++ b/app/Api/Authorization/ActionAuthorization.php @@ -0,0 +1,19 @@ +<?php + +namespace Kanboard\Api\Authorization; + +/** + * Class ActionAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class ActionAuthorization extends ProjectAuthorization +{ + public function check($class, $method, $action_id) + { + if ($this->userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->actionModel->getProjectId($action_id)); + } + } +} diff --git a/app/Api/Authorization/CategoryAuthorization.php b/app/Api/Authorization/CategoryAuthorization.php new file mode 100644 index 00000000..f17265a2 --- /dev/null +++ b/app/Api/Authorization/CategoryAuthorization.php @@ -0,0 +1,19 @@ +<?php + +namespace Kanboard\Api\Authorization; + +/** + * Class CategoryAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class CategoryAuthorization extends ProjectAuthorization +{ + public function check($class, $method, $category_id) + { + if ($this->userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->categoryModel->getProjectId($category_id)); + } + } +} diff --git a/app/Api/Authorization/ColumnAuthorization.php b/app/Api/Authorization/ColumnAuthorization.php new file mode 100644 index 00000000..37aecda2 --- /dev/null +++ b/app/Api/Authorization/ColumnAuthorization.php @@ -0,0 +1,19 @@ +<?php + +namespace Kanboard\Api\Authorization; + +/** + * Class ColumnAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class ColumnAuthorization extends ProjectAuthorization +{ + public function check($class, $method, $column_id) + { + if ($this->userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->columnModel->getProjectId($column_id)); + } + } +} diff --git a/app/Api/Authorization/CommentAuthorization.php b/app/Api/Authorization/CommentAuthorization.php new file mode 100644 index 00000000..ed15512e --- /dev/null +++ b/app/Api/Authorization/CommentAuthorization.php @@ -0,0 +1,19 @@ +<?php + +namespace Kanboard\Api\Authorization; + +/** + * Class CommentAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class CommentAuthorization extends ProjectAuthorization +{ + public function check($class, $method, $comment_id) + { + if ($this->userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->commentModel->getProjectId($comment_id)); + } + } +} diff --git a/app/Api/Authorization/ProcedureAuthorization.php b/app/Api/Authorization/ProcedureAuthorization.php new file mode 100644 index 00000000..070a6371 --- /dev/null +++ b/app/Api/Authorization/ProcedureAuthorization.php @@ -0,0 +1,32 @@ +<?php + +namespace Kanboard\Api\Authorization; + +use JsonRPC\Exception\AccessDeniedException; +use Kanboard\Core\Base; + +/** + * Class ProcedureAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class ProcedureAuthorization extends Base +{ + private $userSpecificProcedures = array( + 'getMe', + 'getMyDashboard', + 'getMyActivityStream', + 'createMyPrivateProject', + 'getMyProjectsList', + 'getMyProjects', + 'getMyOverdueTasks', + ); + + public function check($procedure) + { + if (! $this->userSession->isLogged() && in_array($procedure, $this->userSpecificProcedures)) { + throw new AccessDeniedException('This procedure is not available with the API credentials'); + } + } +} diff --git a/app/Api/Authorization/ProjectAuthorization.php b/app/Api/Authorization/ProjectAuthorization.php new file mode 100644 index 00000000..21ecf311 --- /dev/null +++ b/app/Api/Authorization/ProjectAuthorization.php @@ -0,0 +1,35 @@ +<?php + +namespace Kanboard\Api\Authorization; + +use JsonRPC\Exception\AccessDeniedException; +use Kanboard\Core\Base; + +/** + * Class ProjectAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class ProjectAuthorization extends Base +{ + public function check($class, $method, $project_id) + { + if ($this->userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $project_id); + } + } + + protected function checkProjectPermission($class, $method, $project_id) + { + if (empty($project_id)) { + throw new AccessDeniedException('Project not found'); + } + + $role = $this->projectUserRoleModel->getUserRole($project_id, $this->userSession->getId()); + + if (! $this->apiProjectAuthorization->isAllowed($class, $method, $role)) { + throw new AccessDeniedException('Project access denied'); + } + } +} diff --git a/app/Api/Authorization/SubtaskAuthorization.php b/app/Api/Authorization/SubtaskAuthorization.php new file mode 100644 index 00000000..fcb57929 --- /dev/null +++ b/app/Api/Authorization/SubtaskAuthorization.php @@ -0,0 +1,19 @@ +<?php + +namespace Kanboard\Api\Authorization; + +/** + * Class SubtaskAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class SubtaskAuthorization extends ProjectAuthorization +{ + public function check($class, $method, $subtask_id) + { + if ($this->userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->subtaskModel->getProjectId($subtask_id)); + } + } +} diff --git a/app/Api/Authorization/TaskAuthorization.php b/app/Api/Authorization/TaskAuthorization.php new file mode 100644 index 00000000..db93b76b --- /dev/null +++ b/app/Api/Authorization/TaskAuthorization.php @@ -0,0 +1,19 @@ +<?php + +namespace Kanboard\Api\Authorization; + +/** + * Class TaskAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class TaskAuthorization extends ProjectAuthorization +{ + public function check($class, $method, $category_id) + { + if ($this->userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->taskFinderModel->getProjectId($category_id)); + } + } +} diff --git a/app/Api/Authorization/TaskFileAuthorization.php b/app/Api/Authorization/TaskFileAuthorization.php new file mode 100644 index 00000000..e40783eb --- /dev/null +++ b/app/Api/Authorization/TaskFileAuthorization.php @@ -0,0 +1,19 @@ +<?php + +namespace Kanboard\Api\Authorization; + +/** + * Class TaskFileAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class TaskFileAuthorization extends ProjectAuthorization +{ + public function check($class, $method, $file_id) + { + if ($this->userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->taskFileModel->getProjectId($file_id)); + } + } +} diff --git a/app/Api/Authorization/TaskLinkAuthorization.php b/app/Api/Authorization/TaskLinkAuthorization.php new file mode 100644 index 00000000..2f5fc8d5 --- /dev/null +++ b/app/Api/Authorization/TaskLinkAuthorization.php @@ -0,0 +1,19 @@ +<?php + +namespace Kanboard\Api\Authorization; + +/** + * Class TaskLinkAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class TaskLinkAuthorization extends ProjectAuthorization +{ + public function check($class, $method, $task_link_id) + { + if ($this->userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->taskLinkModel->getProjectId($task_link_id)); + } + } +} diff --git a/app/Api/Authorization/UserAuthorization.php b/app/Api/Authorization/UserAuthorization.php new file mode 100644 index 00000000..3fd6865c --- /dev/null +++ b/app/Api/Authorization/UserAuthorization.php @@ -0,0 +1,22 @@ +<?php + +namespace Kanboard\Api\Authorization; + +use JsonRPC\Exception\AccessDeniedException; +use Kanboard\Core\Base; + +/** + * Class UserAuthorization + * + * @package Kanboard\Api\Authorization + * @author Frederic Guillot + */ +class UserAuthorization extends Base +{ + public function check($class, $method) + { + if ($this->userSession->isLogged() && ! $this->apiAuthorization->isAllowed($class, $method, $this->userSession->getRole())) { + throw new AccessDeniedException('You are not allowed to access to this resource'); + } + } +} diff --git a/app/Api/Middleware/AuthenticationApiMiddleware.php b/app/Api/Middleware/AuthenticationMiddleware.php index b16e10b8..8e309593 100644 --- a/app/Api/Middleware/AuthenticationApiMiddleware.php +++ b/app/Api/Middleware/AuthenticationMiddleware.php @@ -13,46 +13,8 @@ use Kanboard\Core\Base; * @package Kanboard\Api\Middleware * @author Frederic Guillot */ -class AuthenticationApiMiddleware extends Base implements MiddlewareInterface +class AuthenticationMiddleware extends Base implements MiddlewareInterface { - private $user_allowed_procedures = array( - 'getMe', - 'getMyDashboard', - 'getMyActivityStream', - 'createMyPrivateProject', - 'getMyProjectsList', - 'getMyProjects', - 'getMyOverdueTasks', - ); - - private $both_allowed_procedures = array( - 'getTimezone', - 'getVersion', - 'getDefaultTaskColor', - 'getDefaultTaskColors', - 'getColorList', - 'getProjectById', - 'getSubTask', - 'getTask', - 'getTaskByReference', - 'getTimeSpent', - 'getAllTasks', - 'getAllSubTasks', - 'hasTimer', - 'logStartTime', - 'logEndTime', - 'openTask', - 'closeTask', - 'moveTaskPosition', - 'createTask', - 'createSubtask', - 'updateTask', - 'getBoard', - 'getProjectActivity', - 'getOverdueTasksByProject', - 'searchTasks', - ); - /** * Execute Middleware * @@ -68,11 +30,8 @@ class AuthenticationApiMiddleware extends Base implements MiddlewareInterface $this->dispatcher->dispatch('app.bootstrap'); if ($this->isUserAuthenticated($username, $password)) { - $this->checkProcedurePermission(true, $procedureName); $this->userSession->initialize($this->userModel->getByUsername($username)); - } elseif ($this->isAppAuthenticated($username, $password)) { - $this->checkProcedurePermission(false, $procedureName); - } else { + } elseif (! $this->isAppAuthenticated($username, $password)) { $this->logger->error('API authentication failure for '.$username); throw new AuthenticationFailureException('Wrong credentials'); } @@ -120,18 +79,4 @@ class AuthenticationApiMiddleware extends Base implements MiddlewareInterface return $this->configModel->get('api_token'); } - - public function checkProcedurePermission($is_user, $procedure) - { - $is_both_procedure = in_array($procedure, $this->both_allowed_procedures); - $is_user_procedure = in_array($procedure, $this->user_allowed_procedures); - - if ($is_user && ! $is_both_procedure && ! $is_user_procedure) { - throw new AccessDeniedException('Permission denied'); - } elseif (! $is_user && ! $is_both_procedure && $is_user_procedure) { - throw new AccessDeniedException('Permission denied'); - } - - $this->logger->debug('API call: '.$procedure); - } } diff --git a/app/Api/ActionApi.php b/app/Api/Procedure/ActionProcedure.php index 116742d8..4043dbb9 100644 --- a/app/Api/ActionApi.php +++ b/app/Api/Procedure/ActionProcedure.php @@ -1,16 +1,17 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; -use Kanboard\Core\Base; +use Kanboard\Api\Authorization\ActionAuthorization; +use Kanboard\Api\Authorization\ProjectAuthorization; /** * Action API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class ActionApi extends Base +class ActionProcedure extends BaseProcedure { public function getAvailableActions() { @@ -29,16 +30,19 @@ class ActionApi extends Base public function removeAction($action_id) { + ActionAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAction', $action_id); return $this->actionModel->remove($action_id); } public function getActions($project_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActions', $project_id); return $this->actionModel->getAllByProject($project_id); } public function createAction($project_id, $event_name, $action_name, array $params) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createAction', $project_id); $values = array( 'project_id' => $project_id, 'event_name' => $event_name, diff --git a/app/Api/AppApi.php b/app/Api/Procedure/AppProcedure.php index 637de5c5..60af4a60 100644 --- a/app/Api/AppApi.php +++ b/app/Api/Procedure/AppProcedure.php @@ -1,16 +1,14 @@ <?php -namespace Kanboard\Api; - -use Kanboard\Core\Base; +namespace Kanboard\Api\Procedure; /** * App API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class AppApi extends Base +class AppProcedure extends BaseProcedure { public function getTimezone() { diff --git a/app/Api/BaseApi.php b/app/Api/Procedure/BaseProcedure.php index 9f69aa65..e31b3027 100644 --- a/app/Api/BaseApi.php +++ b/app/Api/Procedure/BaseProcedure.php @@ -1,30 +1,24 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; -use JsonRPC\Exception\AccessDeniedException; +use Kanboard\Api\Authorization\ProcedureAuthorization; +use Kanboard\Api\Authorization\UserAuthorization; use Kanboard\Core\Base; +use ReflectionClass; /** * Base class * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -abstract class BaseApi extends Base +abstract class BaseProcedure extends Base { - public function checkProjectPermission($project_id) + public function beforeProcedure($procedure) { - if ($this->userSession->isLogged() && ! $this->projectPermissionModel->isUserAllowed($project_id, $this->userSession->getId())) { - throw new AccessDeniedException('Permission denied'); - } - } - - public function checkTaskPermission($task_id) - { - if ($this->userSession->isLogged()) { - $this->checkProjectPermission($this->taskFinderModel->getProjectId($task_id)); - } + ProcedureAuthorization::getInstance($this->container)->check($procedure); + UserAuthorization::getInstance($this->container)->check($this->getClassName(), $procedure); } protected function formatTask($task) @@ -71,4 +65,21 @@ abstract class BaseApi extends Base return $projects; } + + protected function filterValues(array $values) + { + foreach ($values as $key => $value) { + if (is_null($value)) { + unset($values[$key]); + } + } + + return $values; + } + + protected function getClassName() + { + $reflection = new ReflectionClass(get_called_class()); + return $reflection->getShortName(); + } } diff --git a/app/Api/BoardApi.php b/app/Api/Procedure/BoardProcedure.php index 70f21c0e..674b5466 100644 --- a/app/Api/BoardApi.php +++ b/app/Api/Procedure/BoardProcedure.php @@ -1,21 +1,22 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; +use Kanboard\Api\Authorization\ProjectAuthorization; use Kanboard\Formatter\BoardFormatter; /** * Board API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class BoardApi extends BaseApi +class BoardProcedure extends BaseProcedure { public function getBoard($project_id) { - $this->checkProjectPermission($project_id); - + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getBoard', $project_id); + return BoardFormatter::getInstance($this->container) ->withProjectId($project_id) ->withQuery($this->taskFinderModel->getExtendedQuery()) diff --git a/app/Api/CategoryApi.php b/app/Api/Procedure/CategoryProcedure.php index c56cfb35..3ebbd908 100644 --- a/app/Api/CategoryApi.php +++ b/app/Api/Procedure/CategoryProcedure.php @@ -1,34 +1,40 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; -use Kanboard\Core\Base; +use Kanboard\Api\Authorization\CategoryAuthorization; +use Kanboard\Api\Authorization\ProjectAuthorization; /** * Category API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class CategoryApi extends Base +class CategoryProcedure extends BaseProcedure { public function getCategory($category_id) { + CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'getCategory', $category_id); return $this->categoryModel->getById($category_id); } public function getAllCategories($project_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllCategories', $project_id); return $this->categoryModel->getAll($project_id); } public function removeCategory($category_id) { + CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeCategory', $category_id); return $this->categoryModel->remove($category_id); } public function createCategory($project_id, $name) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createCategory', $project_id); + $values = array( 'project_id' => $project_id, 'name' => $name, @@ -40,6 +46,8 @@ class CategoryApi extends Base public function updateCategory($id, $name) { + CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateCategory', $id); + $values = array( 'id' => $id, 'name' => $name, diff --git a/app/Api/ColumnApi.php b/app/Api/Procedure/ColumnProcedure.php index aa4026f6..ab9d173b 100644 --- a/app/Api/ColumnApi.php +++ b/app/Api/Procedure/ColumnProcedure.php @@ -1,42 +1,51 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; + +use Kanboard\Api\Authorization\ColumnAuthorization; +use Kanboard\Api\Authorization\ProjectAuthorization; /** * Column API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class ColumnApi extends BaseApi +class ColumnProcedure extends BaseProcedure { public function getColumns($project_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumns', $project_id); return $this->columnModel->getAll($project_id); } public function getColumn($column_id) { + ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumn', $column_id); return $this->columnModel->getById($column_id); } public function updateColumn($column_id, $title, $task_limit = 0, $description = '') { + ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateColumn', $column_id); return $this->columnModel->update($column_id, $title, $task_limit, $description); } public function addColumn($project_id, $title, $task_limit = 0, $description = '') { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addColumn', $project_id); return $this->columnModel->create($project_id, $title, $task_limit, $description); } public function removeColumn($column_id) { + ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeColumn', $column_id); return $this->columnModel->remove($column_id); } public function changeColumnPosition($project_id, $column_id, $position) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeColumnPosition', $project_id); return $this->columnModel->changePosition($project_id, $column_id, $position); } } diff --git a/app/Api/CommentApi.php b/app/Api/Procedure/CommentProcedure.php index 8358efee..019a49bb 100644 --- a/app/Api/CommentApi.php +++ b/app/Api/Procedure/CommentProcedure.php @@ -1,34 +1,40 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; -use Kanboard\Core\Base; +use Kanboard\Api\Authorization\CommentAuthorization; +use Kanboard\Api\Authorization\TaskAuthorization; /** * Comment API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class CommentApi extends Base +class CommentProcedure extends BaseProcedure { public function getComment($comment_id) { + CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'getComment', $comment_id); return $this->commentModel->getById($comment_id); } public function getAllComments($task_id) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllComments', $task_id); return $this->commentModel->getAll($task_id); } public function removeComment($comment_id) { + CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeComment', $comment_id); return $this->commentModel->remove($comment_id); } public function createComment($task_id, $user_id, $content, $reference = '') { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createComment', $task_id); + $values = array( 'task_id' => $task_id, 'user_id' => $user_id, @@ -43,6 +49,8 @@ class CommentApi extends Base public function updateComment($id, $content) { + CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateComment', $id); + $values = array( 'id' => $id, 'comment' => $content, diff --git a/app/Api/GroupMemberApi.php b/app/Api/Procedure/GroupMemberProcedure.php index e09f6975..081d6ac8 100644 --- a/app/Api/GroupMemberApi.php +++ b/app/Api/Procedure/GroupMemberProcedure.php @@ -1,16 +1,14 @@ <?php -namespace Kanboard\Api; - -use Kanboard\Core\Base; +namespace Kanboard\Api\Procedure; /** * Group Member API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class GroupMemberApi extends Base +class GroupMemberProcedure extends BaseProcedure { public function getMemberGroups($user_id) { diff --git a/app/Api/GroupApi.php b/app/Api/Procedure/GroupProcedure.php index 1701edc3..804940a2 100644 --- a/app/Api/GroupApi.php +++ b/app/Api/Procedure/GroupProcedure.php @@ -1,16 +1,14 @@ <?php -namespace Kanboard\Api; - -use Kanboard\Core\Base; +namespace Kanboard\Api\Procedure; /** * Group API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class GroupApi extends Base +class GroupProcedure extends BaseProcedure { public function createGroup($name, $external_id = '') { diff --git a/app/Api/LinkApi.php b/app/Api/Procedure/LinkProcedure.php index d8e525e4..b4cecf3a 100644 --- a/app/Api/LinkApi.php +++ b/app/Api/Procedure/LinkProcedure.php @@ -1,16 +1,14 @@ <?php -namespace Kanboard\Api; - -use Kanboard\Core\Base; +namespace Kanboard\Api\Procedure; /** * Link API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class LinkApi extends Base +class LinkProcedure extends BaseProcedure { /** * Get a link by id diff --git a/app/Api/MeApi.php b/app/Api/Procedure/MeProcedure.php index 497749b6..e59e6522 100644 --- a/app/Api/MeApi.php +++ b/app/Api/Procedure/MeProcedure.php @@ -1,16 +1,16 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; use Kanboard\Model\SubtaskModel; /** * Me API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class MeApi extends BaseApi +class MeProcedure extends BaseProcedure { public function getMe() { diff --git a/app/Api/ProjectPermissionApi.php b/app/Api/Procedure/ProjectPermissionProcedure.php index 703cd0f3..e22e1d62 100644 --- a/app/Api/ProjectPermissionApi.php +++ b/app/Api/Procedure/ProjectPermissionProcedure.php @@ -1,73 +1,69 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; -use Kanboard\Core\Base; +use Kanboard\Api\Authorization\ProjectAuthorization; use Kanboard\Core\Security\Role; /** * Project Permission API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class ProjectPermissionApi extends Base +class ProjectPermissionProcedure extends BaseProcedure { public function getProjectUsers($project_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUsers', $project_id); return $this->projectUserRoleModel->getAllUsers($project_id); } public function getAssignableUsers($project_id, $prepend_unassigned = false) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAssignableUsers', $project_id); return $this->projectUserRoleModel->getAssignableUsersList($project_id, $prepend_unassigned); } public function addProjectUser($project_id, $user_id, $role = Role::PROJECT_MEMBER) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectUser', $project_id); return $this->projectUserRoleModel->addUser($project_id, $user_id, $role); } public function addProjectGroup($project_id, $group_id, $role = Role::PROJECT_MEMBER) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectGroup', $project_id); return $this->projectGroupRoleModel->addGroup($project_id, $group_id, $role); } public function removeProjectUser($project_id, $user_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectUser', $project_id); return $this->projectUserRoleModel->removeUser($project_id, $user_id); } public function removeProjectGroup($project_id, $group_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectGroup', $project_id); return $this->projectGroupRoleModel->removeGroup($project_id, $group_id); } public function changeProjectUserRole($project_id, $user_id, $role) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectUserRole', $project_id); return $this->projectUserRoleModel->changeUserRole($project_id, $user_id, $role); } public function changeProjectGroupRole($project_id, $group_id, $role) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectGroupRole', $project_id); return $this->projectGroupRoleModel->changeGroupRole($project_id, $group_id, $role); } - // Deprecated - public function getMembers($project_id) + public function getProjectUserRole($project_id, $user_id) { - return $this->getProjectUsers($project_id); - } - - // Deprecated - public function revokeUser($project_id, $user_id) - { - return $this->removeProjectUser($project_id, $user_id); - } - - // Deprecated - public function allowUser($project_id, $user_id) - { - return $this->addProjectUser($project_id, $user_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUserRole', $project_id); + return $this->projectUserRoleModel->getUserRole($project_id, $user_id); } } diff --git a/app/Api/Procedure/ProjectProcedure.php b/app/Api/Procedure/ProjectProcedure.php new file mode 100644 index 00000000..a580c8d9 --- /dev/null +++ b/app/Api/Procedure/ProjectProcedure.php @@ -0,0 +1,113 @@ +<?php + +namespace Kanboard\Api\Procedure; + +use Kanboard\Api\Authorization\ProjectAuthorization; + +/** + * Project API controller + * + * @package Kanboard\Api\Procedure + * @author Frederic Guillot + */ +class ProjectProcedure extends BaseProcedure +{ + public function getProjectById($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectById', $project_id); + return $this->formatProject($this->projectModel->getById($project_id)); + } + + public function getProjectByName($name) + { + $project = $this->projectModel->getByName($name); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByName', $project['id']); + return $this->formatProject($project); + } + + public function getProjectByIdentifier($identifier) + { + $project = $this->formatProject($this->projectModel->getByIdentifier($identifier)); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByIdentifier', $project['id']); + return $this->formatProject($project); + } + + public function getAllProjects() + { + return $this->formatProjects($this->projectModel->getAll()); + } + + public function removeProject($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProject', $project_id); + return $this->projectModel->remove($project_id); + } + + public function enableProject($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProject', $project_id); + return $this->projectModel->enable($project_id); + } + + public function disableProject($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProject', $project_id); + return $this->projectModel->disable($project_id); + } + + public function enableProjectPublicAccess($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProjectPublicAccess', $project_id); + return $this->projectModel->enablePublicAccess($project_id); + } + + public function disableProjectPublicAccess($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProjectPublicAccess', $project_id); + return $this->projectModel->disablePublicAccess($project_id); + } + + public function getProjectActivities(array $project_ids) + { + foreach ($project_ids as $project_id) { + ProjectAuthorization::getInstance($this->container) + ->check($this->getClassName(), 'getProjectActivities', $project_id); + } + + return $this->helper->projectActivity->getProjectsEvents($project_ids); + } + + public function getProjectActivity($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectActivity', $project_id); + return $this->helper->projectActivity->getProjectEvents($project_id); + } + + public function createProject($name, $description = null, $owner_id = 0, $identifier = null) + { + $values = $this->filterValues(array( + 'name' => $name, + 'description' => $description, + 'identifier' => $identifier, + )); + + list($valid, ) = $this->projectValidator->validateCreation($values); + return $valid ? $this->projectModel->create($values, $owner_id, $this->userSession->isLogged()) : false; + } + + public function updateProject($project_id, $name = null, $description = null, $owner_id = null, $identifier = null) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateProject', $project_id); + + $values = $this->filterValues(array( + 'id' => $project_id, + 'name' => $name, + 'description' => $description, + 'owner_id' => $owner_id, + 'identifier' => $identifier, + )); + + list($valid, ) = $this->projectValidator->validateModification($values); + return $valid && $this->projectModel->update($values); + } +} diff --git a/app/Api/SubtaskApi.php b/app/Api/Procedure/SubtaskProcedure.php index 5764ff7d..e2400912 100644 --- a/app/Api/SubtaskApi.php +++ b/app/Api/Procedure/SubtaskProcedure.php @@ -1,34 +1,40 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; -use Kanboard\Core\Base; +use Kanboard\Api\Authorization\SubtaskAuthorization; +use Kanboard\Api\Authorization\TaskAuthorization; /** * Subtask API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class SubtaskApi extends Base +class SubtaskProcedure extends BaseProcedure { public function getSubtask($subtask_id) { + SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtask', $subtask_id); return $this->subtaskModel->getById($subtask_id); } public function getAllSubtasks($task_id) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSubtasks', $task_id); return $this->subtaskModel->getAll($task_id); } public function removeSubtask($subtask_id) { + SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSubtask', $subtask_id); return $this->subtaskModel->remove($subtask_id); } public function createSubtask($task_id, $title, $user_id = 0, $time_estimated = 0, $time_spent = 0, $status = 0) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createSubtask', $task_id); + $values = array( 'title' => $title, 'task_id' => $task_id, @@ -44,6 +50,8 @@ class SubtaskApi extends Base public function updateSubtask($id, $task_id, $title = null, $user_id = null, $time_estimated = null, $time_spent = null, $status = null) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateSubtask', $task_id); + $values = array( 'id' => $id, 'task_id' => $task_id, diff --git a/app/Api/Procedure/SubtaskTimeTrackingProcedure.php b/app/Api/Procedure/SubtaskTimeTrackingProcedure.php new file mode 100644 index 00000000..5d1988d6 --- /dev/null +++ b/app/Api/Procedure/SubtaskTimeTrackingProcedure.php @@ -0,0 +1,39 @@ +<?php + +namespace Kanboard\Api\Procedure; + +use Kanboard\Api\Authorization\SubtaskAuthorization; + +/** + * Subtask Time Tracking API controller + * + * @package Kanboard\Api\Procedure + * @author Frederic Guillot + * @author Nikolaos Georgakis + */ +class SubtaskTimeTrackingProcedure extends BaseProcedure +{ + public function hasSubtaskTimer($subtask_id, $user_id) + { + SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'hasSubtaskTimer', $subtask_id); + return $this->subtaskTimeTrackingModel->hasTimer($subtask_id, $user_id); + } + + public function logSubtaskStartTime($subtask_id, $user_id) + { + SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'logSubtaskStartTime', $subtask_id); + return $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $user_id); + } + + public function logSubtaskEndTime($subtask_id,$user_id) + { + SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'logSubtaskEndTime', $subtask_id); + return $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $user_id); + } + + public function getSubtaskTimeSpent($subtask_id,$user_id) + { + SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtaskTimeSpent', $subtask_id); + return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id, $user_id); + } +} diff --git a/app/Api/SwimlaneApi.php b/app/Api/Procedure/SwimlaneProcedure.php index c3c56a71..9b7d181d 100644 --- a/app/Api/SwimlaneApi.php +++ b/app/Api/Procedure/SwimlaneProcedure.php @@ -1,34 +1,39 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; -use Kanboard\Core\Base; +use Kanboard\Api\Authorization\ProjectAuthorization; /** * Swimlane API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class SwimlaneApi extends Base +class SwimlaneProcedure extends BaseProcedure { public function getActiveSwimlanes($project_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActiveSwimlanes', $project_id); return $this->swimlaneModel->getSwimlanes($project_id); } public function getAllSwimlanes($project_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSwimlanes', $project_id); return $this->swimlaneModel->getAll($project_id); } public function getSwimlaneById($swimlane_id) { - return $this->swimlaneModel->getById($swimlane_id); + $swimlane = $this->swimlaneModel->getById($swimlane_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneById', $swimlane['project_id']); + return $swimlane; } public function getSwimlaneByName($project_id, $name) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneByName', $project_id); return $this->swimlaneModel->getByName($project_id, $name); } @@ -39,11 +44,13 @@ class SwimlaneApi extends Base public function getDefaultSwimlane($project_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getDefaultSwimlane', $project_id); return $this->swimlaneModel->getDefault($project_id); } public function addSwimlane($project_id, $name, $description = '') { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addSwimlane', $project_id); return $this->swimlaneModel->create(array('project_id' => $project_id, 'name' => $name, 'description' => $description)); } @@ -60,21 +67,25 @@ class SwimlaneApi extends Base public function removeSwimlane($project_id, $swimlane_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSwimlane', $project_id); return $this->swimlaneModel->remove($project_id, $swimlane_id); } public function disableSwimlane($project_id, $swimlane_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableSwimlane', $project_id); return $this->swimlaneModel->disable($project_id, $swimlane_id); } public function enableSwimlane($project_id, $swimlane_id) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableSwimlane', $project_id); return $this->swimlaneModel->enable($project_id, $swimlane_id); } public function changeSwimlanePosition($project_id, $swimlane_id, $position) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeSwimlanePosition', $project_id); return $this->swimlaneModel->changePosition($project_id, $swimlane_id, $position); } } diff --git a/app/Api/FileApi.php b/app/Api/Procedure/TaskFileProcedure.php index 1ed3aeb9..5aa7ea0b 100644 --- a/app/Api/FileApi.php +++ b/app/Api/Procedure/TaskFileProcedure.php @@ -1,29 +1,36 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; +use Kanboard\Api\Authorization\ProjectAuthorization; +use Kanboard\Api\Authorization\TaskAuthorization; +use Kanboard\Api\Authorization\TaskFileAuthorization; use Kanboard\Core\ObjectStorage\ObjectStorageException; /** - * File API controller + * Task File API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class FileApi extends BaseApi +class TaskFileProcedure extends BaseProcedure { public function getTaskFile($file_id) { + TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskFile', $file_id); return $this->taskFileModel->getById($file_id); } public function getAllTaskFiles($task_id) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskFiles', $task_id); return $this->taskFileModel->getAll($task_id); } public function downloadTaskFile($file_id) { + TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadTaskFile', $file_id); + try { $file = $this->taskFileModel->getById($file_id); @@ -33,12 +40,14 @@ class FileApi extends BaseApi } catch (ObjectStorageException $e) { $this->logger->error($e->getMessage()); } - + return ''; } public function createTaskFile($project_id, $task_id, $filename, $blob) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskFile', $project_id); + try { return $this->taskFileModel->uploadContent($task_id, $filename, $blob); } catch (ObjectStorageException $e) { @@ -49,43 +58,13 @@ class FileApi extends BaseApi public function removeTaskFile($file_id) { + TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskFile', $file_id); return $this->taskFileModel->remove($file_id); } public function removeAllTaskFiles($task_id) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllTaskFiles', $task_id); return $this->taskFileModel->removeAll($task_id); } - - // Deprecated procedures - - public function getFile($file_id) - { - return $this->getTaskFile($file_id); - } - - public function getAllFiles($task_id) - { - return $this->getAllTaskFiles($task_id); - } - - public function downloadFile($file_id) - { - return $this->downloadTaskFile($file_id); - } - - public function createFile($project_id, $task_id, $filename, $blob) - { - return $this->createTaskFile($project_id, $task_id, $filename, $blob); - } - - public function removeFile($file_id) - { - return $this->removeTaskFile($file_id); - } - - public function removeAllFiles($task_id) - { - return $this->removeAllTaskFiles($task_id); - } } diff --git a/app/Api/TaskLinkApi.php b/app/Api/Procedure/TaskLinkProcedure.php index bb809133..375266fb 100644 --- a/app/Api/TaskLinkApi.php +++ b/app/Api/Procedure/TaskLinkProcedure.php @@ -1,16 +1,17 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; -use Kanboard\Core\Base; +use Kanboard\Api\Authorization\TaskAuthorization; +use Kanboard\Api\Authorization\TaskLinkAuthorization; /** * TaskLink API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class TaskLinkApi extends Base +class TaskLinkProcedure extends BaseProcedure { /** * Get a task link @@ -21,6 +22,7 @@ class TaskLinkApi extends Base */ public function getTaskLinkById($task_link_id) { + TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskLinkById', $task_link_id); return $this->taskLinkModel->getById($task_link_id); } @@ -33,6 +35,7 @@ class TaskLinkApi extends Base */ public function getAllTaskLinks($task_id) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskLinks', $task_id); return $this->taskLinkModel->getAll($task_id); } @@ -47,6 +50,7 @@ class TaskLinkApi extends Base */ public function createTaskLink($task_id, $opposite_task_id, $link_id) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskLink', $task_id); return $this->taskLinkModel->create($task_id, $opposite_task_id, $link_id); } @@ -62,6 +66,7 @@ class TaskLinkApi extends Base */ public function updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTaskLink', $task_id); return $this->taskLinkModel->update($task_link_id, $task_id, $opposite_task_id, $link_id); } @@ -74,6 +79,7 @@ class TaskLinkApi extends Base */ public function removeTaskLink($task_link_id) { + TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskLink', $task_link_id); return $this->taskLinkModel->remove($task_link_id); } } diff --git a/app/Api/TaskApi.php b/app/Api/Procedure/TaskProcedure.php index ddb3ac54..2d29a4ef 100644 --- a/app/Api/TaskApi.php +++ b/app/Api/Procedure/TaskProcedure.php @@ -1,39 +1,41 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; +use Kanboard\Api\Authorization\ProjectAuthorization; +use Kanboard\Api\Authorization\TaskAuthorization; use Kanboard\Filter\TaskProjectFilter; use Kanboard\Model\TaskModel; /** * Task API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class TaskApi extends BaseApi +class TaskProcedure extends BaseProcedure { public function searchTasks($project_id, $query) { - $this->checkProjectPermission($project_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'searchTasks', $project_id); return $this->taskLexer->build($query)->withFilter(new TaskProjectFilter($project_id))->toArray(); } public function getTask($task_id) { - $this->checkTaskPermission($task_id); + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTask', $task_id); return $this->formatTask($this->taskFinderModel->getById($task_id)); } public function getTaskByReference($project_id, $reference) { - $this->checkProjectPermission($project_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskByReference', $project_id); return $this->formatTask($this->taskFinderModel->getByReference($project_id, $reference)); } public function getAllTasks($project_id, $status_id = TaskModel::STATUS_OPEN) { - $this->checkProjectPermission($project_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTasks', $project_id); return $this->formatTasks($this->taskFinderModel->getAll($project_id, $status_id)); } @@ -44,40 +46,43 @@ class TaskApi extends BaseApi public function getOverdueTasksByProject($project_id) { - $this->checkProjectPermission($project_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getOverdueTasksByProject', $project_id); return $this->taskFinderModel->getOverdueTasksByProject($project_id); } public function openTask($task_id) { - $this->checkTaskPermission($task_id); + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'openTask', $task_id); return $this->taskStatusModel->open($task_id); } public function closeTask($task_id) { - $this->checkTaskPermission($task_id); + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'closeTask', $task_id); return $this->taskStatusModel->close($task_id); } public function removeTask($task_id) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTask', $task_id); return $this->taskModel->remove($task_id); } public function moveTaskPosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0) { - $this->checkProjectPermission($project_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskPosition', $project_id); return $this->taskPositionModel->movePosition($project_id, $task_id, $column_id, $position, $swimlane_id); } public function moveTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskToProject', $project_id); return $this->taskDuplicationModel->moveToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id); } public function duplicateTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'duplicateTaskToProject', $project_id); return $this->taskDuplicationModel->duplicateToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id); } @@ -86,8 +91,8 @@ class TaskApi extends BaseApi $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0, $recurrence_basedate = 0, $reference = '') { - $this->checkProjectPermission($project_id); - + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTask', $project_id); + if ($owner_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) { return false; } @@ -127,8 +132,7 @@ class TaskApi extends BaseApi $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null, $recurrence_timeframe = null, $recurrence_basedate = null, $reference = null) { - $this->checkTaskPermission($id); - + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $id); $project_id = $this->taskFinderModel->getProjectId($id); if ($project_id === 0) { @@ -139,7 +143,7 @@ class TaskApi extends BaseApi return false; } - $values = array( + $values = $this->filterValues(array( 'id' => $id, 'title' => $title, 'color_id' => $color_id, @@ -155,13 +159,7 @@ class TaskApi extends BaseApi 'recurrence_basedate' => $recurrence_basedate, 'reference' => $reference, 'priority' => $priority, - ); - - foreach ($values as $key => $value) { - if (is_null($value)) { - unset($values[$key]); - } - } + )); list($valid) = $this->taskValidator->validateApiModification($values); return $valid && $this->taskModificationModel->update($values); diff --git a/app/Api/UserApi.php b/app/Api/Procedure/UserProcedure.php index 88d75527..145f85bf 100644 --- a/app/Api/UserApi.php +++ b/app/Api/Procedure/UserProcedure.php @@ -1,8 +1,7 @@ <?php -namespace Kanboard\Api; +namespace Kanboard\Api\Procedure; -use Kanboard\Core\Base; use LogicException; use Kanboard\Core\Security\Role; use Kanboard\Core\Ldap\Client as LdapClient; @@ -12,10 +11,10 @@ use Kanboard\Core\Ldap\User as LdapUser; /** * User API controller * - * @package Kanboard\Api + * @package Kanboard\Api\Procedure * @author Frederic Guillot */ -class UserApi extends Base +class UserProcedure extends BaseProcedure { public function getUser($user_id) { @@ -118,19 +117,13 @@ class UserApi extends Base public function updateUser($id, $username = null, $name = null, $email = null, $role = null) { - $values = array( + $values = $this->filterValues(array( 'id' => $id, 'username' => $username, 'name' => $name, 'email' => $email, 'role' => $role, - ); - - foreach ($values as $key => $value) { - if (is_null($value)) { - unset($values[$key]); - } - } + )); list($valid, ) = $this->userValidator->validateApiModification($values); return $valid && $this->userModel->update($values); diff --git a/app/Api/ProjectApi.php b/app/Api/ProjectApi.php deleted file mode 100644 index 29a9cd79..00000000 --- a/app/Api/ProjectApi.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php - -namespace Kanboard\Api; - -/** - * Project API controller - * - * @package Kanboard\Api - * @author Frederic Guillot - */ -class ProjectApi extends BaseApi -{ - public function getProjectById($project_id) - { - $this->checkProjectPermission($project_id); - return $this->formatProject($this->projectModel->getById($project_id)); - } - - public function getProjectByName($name) - { - return $this->formatProject($this->projectModel->getByName($name)); - } - - public function getAllProjects() - { - return $this->formatProjects($this->projectModel->getAll()); - } - - public function removeProject($project_id) - { - return $this->projectModel->remove($project_id); - } - - public function enableProject($project_id) - { - return $this->projectModel->enable($project_id); - } - - public function disableProject($project_id) - { - return $this->projectModel->disable($project_id); - } - - public function enableProjectPublicAccess($project_id) - { - return $this->projectModel->enablePublicAccess($project_id); - } - - public function disableProjectPublicAccess($project_id) - { - return $this->projectModel->disablePublicAccess($project_id); - } - - public function getProjectActivities(array $project_ids) - { - return $this->helper->projectActivity->getProjectsEvents($project_ids); - } - - public function getProjectActivity($project_id) - { - $this->checkProjectPermission($project_id); - return $this->helper->projectActivity->getProjectEvents($project_id); - } - - public function createProject($name, $description = null) - { - $values = array( - 'name' => $name, - 'description' => $description - ); - - list($valid, ) = $this->projectValidator->validateCreation($values); - return $valid ? $this->projectModel->create($values) : false; - } - - public function updateProject($id, $name, $description = null) - { - $values = array( - 'id' => $id, - 'name' => $name, - 'description' => $description - ); - - list($valid, ) = $this->projectValidator->validateModification($values); - return $valid && $this->projectModel->update($values); - } -} diff --git a/app/Api/SubtaskTimeTrackingApi.php b/app/Api/SubtaskTimeTrackingApi.php deleted file mode 100644 index 0e700b31..00000000 --- a/app/Api/SubtaskTimeTrackingApi.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -namespace Kanboard\Api; - -use Kanboard\Core\Base; - -/** - * Subtask Time Tracking API controller - * - * @package api - * @author Nikolaos Georgakis - */ -class SubtaskTimeTrackingApi extends Base -{ - public function hasTimer($subtask_id,$user_id) - { - return $this->subtaskTimeTrackingModel->hasTimer($subtask_id,$user_id); - } - - public function logStartTime($subtask_id,$user_id) - { - return $this->subtaskTimeTrackingModel->logStartTime($subtask_id,$user_id); - } - - public function logEndTime($subtask_id,$user_id) - { - return $this->subtaskTimeTrackingModel->logEndTime($subtask_id,$user_id); - } - - public function getTimeSpent($subtask_id,$user_id) - { - return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id,$user_id); - } -} diff --git a/app/Core/Base.php b/app/Core/Base.php index e5dd6ad9..eacca65d 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -35,8 +35,12 @@ use Pimple\Container; * @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager * @property \Kanboard\Core\Security\AccessMap $applicationAccessMap * @property \Kanboard\Core\Security\AccessMap $projectAccessMap + * @property \Kanboard\Core\Security\AccessMap $apiAccessMap + * @property \Kanboard\Core\Security\AccessMap $apiProjectAccessMap * @property \Kanboard\Core\Security\Authorization $applicationAuthorization * @property \Kanboard\Core\Security\Authorization $projectAuthorization + * @property \Kanboard\Core\Security\Authorization $apiAuthorization + * @property \Kanboard\Core\Security\Authorization $apiProjectAuthorization * @property \Kanboard\Core\Security\Role $role * @property \Kanboard\Core\Security\Token $token * @property \Kanboard\Core\Session\FlashMessage $flash diff --git a/app/Model/ActionModel.php b/app/Model/ActionModel.php index 53393ed5..b5d2bd06 100644 --- a/app/Model/ActionModel.php +++ b/app/Model/ActionModel.php @@ -86,6 +86,18 @@ class ActionModel extends Base } /** + * Get the projectId by the actionId + * + * @access public + * @param integer $action_id + * @return integer + */ + public function getProjectId($action_id) + { + return $this->db->table(self::TABLE)->eq('id', $action_id)->findOneColumn('project_id') ?: 0; + } + + /** * Attach parameters to actions * * @access private diff --git a/app/Model/CategoryModel.php b/app/Model/CategoryModel.php index 62fb5611..024d0026 100644 --- a/app/Model/CategoryModel.php +++ b/app/Model/CategoryModel.php @@ -56,6 +56,18 @@ class CategoryModel extends Base } /** + * Get the projectId by the category id + * + * @access public + * @param integer $category_id Category id + * @return integer + */ + public function getProjectId($category_id) + { + return $this->db->table(self::TABLE)->eq('id', $category_id)->findOneColumn('project_id') ?: 0; + } + + /** * Get a category id by the category name and project id * * @access public diff --git a/app/Model/ColumnModel.php b/app/Model/ColumnModel.php index 1adac0f2..795fe692 100644 --- a/app/Model/ColumnModel.php +++ b/app/Model/ColumnModel.php @@ -32,6 +32,18 @@ class ColumnModel extends Base } /** + * Get projectId by the columnId + * + * @access public + * @param integer $column_id Column id + * @return integer + */ + public function getProjectId($column_id) + { + return $this->db->table(self::TABLE)->eq('id', $column_id)->findOneColumn('project_id'); + } + + /** * Get the first column id for a given project * * @access public diff --git a/app/Model/CommentModel.php b/app/Model/CommentModel.php index 36e1fc48..4231f29d 100644 --- a/app/Model/CommentModel.php +++ b/app/Model/CommentModel.php @@ -30,6 +30,22 @@ class CommentModel extends Base const EVENT_USER_MENTION = 'comment.user.mention'; /** + * Get projectId from commentId + * + * @access public + * @param integer $comment_id + * @return integer + */ + public function getProjectId($comment_id) + { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.id', $comment_id) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0; + } + + /** * Get all comments for a given task * * @access public diff --git a/app/Model/SubtaskModel.php b/app/Model/SubtaskModel.php index 019064ad..a97bddbf 100644 --- a/app/Model/SubtaskModel.php +++ b/app/Model/SubtaskModel.php @@ -52,6 +52,22 @@ class SubtaskModel extends Base const EVENT_DELETE = 'subtask.delete'; /** + * Get projectId from subtaskId + * + * @access public + * @param integer $subtask_id + * @return integer + */ + public function getProjectId($subtask_id) + { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.id', $subtask_id) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0; + } + + /** * Get available status * * @access public diff --git a/app/Model/TaskFileModel.php b/app/Model/TaskFileModel.php index 24c1ad4b..7603019a 100644 --- a/app/Model/TaskFileModel.php +++ b/app/Model/TaskFileModel.php @@ -73,6 +73,22 @@ class TaskFileModel extends FileModel } /** + * Get projectId from fileId + * + * @access public + * @param integer $file_id + * @return integer + */ + public function getProjectId($file_id) + { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.id', $file_id) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0; + } + + /** * Handle screenshot upload * * @access public diff --git a/app/Model/TaskLinkModel.php b/app/Model/TaskLinkModel.php index 45225e35..09978eae 100644 --- a/app/Model/TaskLinkModel.php +++ b/app/Model/TaskLinkModel.php @@ -29,6 +29,22 @@ class TaskLinkModel extends Base const EVENT_CREATE_UPDATE = 'tasklink.create_update'; /** + * Get projectId from $task_link_id + * + * @access public + * @param integer $task_link_id + * @return integer + */ + public function getProjectId($task_link_id) + { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.id', $task_link_id) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0; + } + + /** * Get a task link * * @access public diff --git a/app/ServiceProvider/ApiProvider.php b/app/ServiceProvider/ApiProvider.php index 93b3c7f5..f88d9b4f 100644 --- a/app/ServiceProvider/ApiProvider.php +++ b/app/ServiceProvider/ApiProvider.php @@ -3,26 +3,26 @@ namespace Kanboard\ServiceProvider; use JsonRPC\Server; -use Kanboard\Api\ActionApi; -use Kanboard\Api\AppApi; -use Kanboard\Api\BoardApi; -use Kanboard\Api\CategoryApi; -use Kanboard\Api\ColumnApi; -use Kanboard\Api\CommentApi; -use Kanboard\Api\FileApi; -use Kanboard\Api\GroupApi; -use Kanboard\Api\GroupMemberApi; -use Kanboard\Api\LinkApi; -use Kanboard\Api\MeApi; -use Kanboard\Api\Middleware\AuthenticationApiMiddleware; -use Kanboard\Api\ProjectApi; -use Kanboard\Api\ProjectPermissionApi; -use Kanboard\Api\SubtaskApi; -use Kanboard\Api\SubtaskTimeTrackingApi; -use Kanboard\Api\SwimlaneApi; -use Kanboard\Api\TaskApi; -use Kanboard\Api\TaskLinkApi; -use Kanboard\Api\UserApi; +use Kanboard\Api\Procedure\ActionProcedure; +use Kanboard\Api\Procedure\AppProcedure; +use Kanboard\Api\Procedure\BoardProcedure; +use Kanboard\Api\Procedure\CategoryProcedure; +use Kanboard\Api\Procedure\ColumnProcedure; +use Kanboard\Api\Procedure\CommentProcedure; +use Kanboard\Api\Procedure\TaskFileProcedure; +use Kanboard\Api\Procedure\GroupProcedure; +use Kanboard\Api\Procedure\GroupMemberProcedure; +use Kanboard\Api\Procedure\LinkProcedure; +use Kanboard\Api\Procedure\MeProcedure; +use Kanboard\Api\Middleware\AuthenticationMiddleware; +use Kanboard\Api\Procedure\ProjectProcedure; +use Kanboard\Api\Procedure\ProjectPermissionProcedure; +use Kanboard\Api\Procedure\SubtaskProcedure; +use Kanboard\Api\Procedure\SubtaskTimeTrackingProcedure; +use Kanboard\Api\Procedure\SwimlaneProcedure; +use Kanboard\Api\Procedure\TaskProcedure; +use Kanboard\Api\Procedure\TaskLinkProcedure; +use Kanboard\Api\Procedure\UserProcedure; use Pimple\Container; use Pimple\ServiceProviderInterface; @@ -45,31 +45,32 @@ class ApiProvider implements ServiceProviderInterface $server = new Server(); $server->setAuthenticationHeader(API_AUTHENTICATION_HEADER); $server->getMiddlewareHandler() - ->withMiddleware(new AuthenticationApiMiddleware($container)) + ->withMiddleware(new AuthenticationMiddleware($container)) ; $server->getProcedureHandler() - ->withObject(new MeApi($container)) - ->withObject(new ActionApi($container)) - ->withObject(new AppApi($container)) - ->withObject(new BoardApi($container)) - ->withObject(new ColumnApi($container)) - ->withObject(new CategoryApi($container)) - ->withObject(new CommentApi($container)) - ->withObject(new FileApi($container)) - ->withObject(new LinkApi($container)) - ->withObject(new ProjectApi($container)) - ->withObject(new ProjectPermissionApi($container)) - ->withObject(new SubtaskApi($container)) - ->withObject(new SubtaskTimeTrackingApi($container)) - ->withObject(new SwimlaneApi($container)) - ->withObject(new TaskApi($container)) - ->withObject(new TaskLinkApi($container)) - ->withObject(new UserApi($container)) - ->withObject(new GroupApi($container)) - ->withObject(new GroupMemberApi($container)) + ->withObject(new MeProcedure($container)) + ->withObject(new ActionProcedure($container)) + ->withObject(new AppProcedure($container)) + ->withObject(new BoardProcedure($container)) + ->withObject(new ColumnProcedure($container)) + ->withObject(new CategoryProcedure($container)) + ->withObject(new CommentProcedure($container)) + ->withObject(new TaskFileProcedure($container)) + ->withObject(new LinkProcedure($container)) + ->withObject(new ProjectProcedure($container)) + ->withObject(new ProjectPermissionProcedure($container)) + ->withObject(new SubtaskProcedure($container)) + ->withObject(new SubtaskTimeTrackingProcedure($container)) + ->withObject(new SwimlaneProcedure($container)) + ->withObject(new TaskProcedure($container)) + ->withObject(new TaskLinkProcedure($container)) + ->withObject(new UserProcedure($container)) + ->withObject(new GroupProcedure($container)) + ->withObject(new GroupMemberProcedure($container)) + ->withBeforeMethod('beforeProcedure') ; - + $container['api'] = $server; return $container; } diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php index 84e4354d..751fe514 100644 --- a/app/ServiceProvider/AuthenticationProvider.php +++ b/app/ServiceProvider/AuthenticationProvider.php @@ -46,9 +46,13 @@ class AuthenticationProvider implements ServiceProviderInterface $container['projectAccessMap'] = $this->getProjectAccessMap(); $container['applicationAccessMap'] = $this->getApplicationAccessMap(); + $container['apiAccessMap'] = $this->getApiAccessMap(); + $container['apiProjectAccessMap'] = $this->getApiProjectAccessMap(); $container['projectAuthorization'] = new Authorization($container['projectAccessMap']); $container['applicationAuthorization'] = new Authorization($container['applicationAccessMap']); + $container['apiAuthorization'] = new Authorization($container['apiAccessMap']); + $container['apiProjectAuthorization'] = new Authorization($container['apiProjectAccessMap']); return $container; } @@ -151,4 +155,57 @@ class AuthenticationProvider implements ServiceProviderInterface return $acl; } + + /** + * Get ACL for the API + * + * @access public + * @return AccessMap + */ + public function getApiAccessMap() + { + $acl = new AccessMap; + $acl->setDefaultRole(Role::APP_USER); + $acl->setRoleHierarchy(Role::APP_ADMIN, array(Role::APP_MANAGER, Role::APP_USER, Role::APP_PUBLIC)); + $acl->setRoleHierarchy(Role::APP_MANAGER, array(Role::APP_USER, Role::APP_PUBLIC)); + + $acl->add('UserProcedure', '*', Role::APP_ADMIN); + $acl->add('GroupMemberProcedure', '*', Role::APP_ADMIN); + $acl->add('GroupProcedure', '*', Role::APP_ADMIN); + $acl->add('LinkProcedure', '*', Role::APP_ADMIN); + $acl->add('TaskProcedure', array('getOverdueTasks'), Role::APP_ADMIN); + $acl->add('ProjectProcedure', array('getAllProjects'), Role::APP_ADMIN); + $acl->add('ProjectProcedure', array('createProject'), Role::APP_MANAGER); + + return $acl; + } + + /** + * Get ACL for the API + * + * @access public + * @return AccessMap + */ + public function getApiProjectAccessMap() + { + $acl = new AccessMap; + $acl->setDefaultRole(Role::PROJECT_VIEWER); + $acl->setRoleHierarchy(Role::PROJECT_MANAGER, array(Role::PROJECT_MEMBER, Role::PROJECT_VIEWER)); + $acl->setRoleHierarchy(Role::PROJECT_MEMBER, array(Role::PROJECT_VIEWER)); + + $acl->add('ActionProcedure', array('removeAction', 'getActions', 'createAction'), Role::PROJECT_MANAGER); + $acl->add('CategoryProcedure', '*', Role::PROJECT_MANAGER); + $acl->add('ColumnProcedure', '*', Role::PROJECT_MANAGER); + $acl->add('CommentProcedure', array('removeComment', 'createComment', 'updateComment'), Role::PROJECT_MEMBER); + $acl->add('ProjectPermissionProcedure', '*', Role::PROJECT_MANAGER); + $acl->add('ProjectProcedure', array('updateProject', 'removeProject', 'enableProject', 'disableProject', 'enableProjectPublicAccess', 'disableProjectPublicAccess'), Role::PROJECT_MANAGER); + $acl->add('SubtaskProcedure', '*', Role::PROJECT_MEMBER); + $acl->add('SubtaskTimeTrackingProcedure', '*', Role::PROJECT_MEMBER); + $acl->add('SwimlaneProcedure', '*', Role::PROJECT_MANAGER); + $acl->add('TaskFileProcedure', '*', Role::PROJECT_MEMBER); + $acl->add('TaskLinkProcedure', '*', Role::PROJECT_MEMBER); + $acl->add('TaskProcedure', '*', Role::PROJECT_MEMBER); + + return $acl; + } } diff --git a/app/Validator/ProjectValidator.php b/app/Validator/ProjectValidator.php index 9ef59111..8c6117a4 100644 --- a/app/Validator/ProjectValidator.php +++ b/app/Validator/ProjectValidator.php @@ -28,7 +28,7 @@ class ProjectValidator extends BaseValidator new Validators\Integer('priority_start', t('This value must be an integer')), new Validators\Integer('priority_end', t('This value must be an integer')), new Validators\Integer('is_active', t('This value must be an integer')), - new Validators\Required('name', t('The project name is required')), + new Validators\NotEmpty('name', t('This field cannot be empty')), new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50), new Validators\MaxLength('identifier', t('The maximum length is %d characters', 50), 50), new Validators\MaxLength('start_date', t('The maximum length is %d characters', 10), 10), @@ -47,11 +47,15 @@ class ProjectValidator extends BaseValidator */ public function validateCreation(array $values) { + $rules = array( + new Validators\Required('name', t('The project name is required')), + ); + if (! empty($values['identifier'])) { $values['identifier'] = strtoupper($values['identifier']); } - $v = new Validator($values, $this->commonValidationRules()); + $v = new Validator($values, array_merge($this->commonValidationRules(), $rules)); return array( $v->execute(), diff --git a/composer.json b/composer.json index 85fdb5ad..d82f3f0c 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "discard-changes": true }, "require" : { - "php" : ">=5.3.3", + "php" : ">=5.3.9", "ext-gd" : "*", "ext-mbstring" : "*", "ext-hash" : "*", @@ -23,21 +23,21 @@ "ext-ctype" : "*", "ext-filter" : "*", "ext-session" : "*", - "christian-riesen/otp" : "1.4", - "eluceo/ical": "0.8.0", + "christian-riesen/otp" : "1.4.3", + "eluceo/ical": "0.10.1", "erusev/parsedown" : "1.6.0", - "fguillot/json-rpc" : "1.2.0", + "fguillot/json-rpc" : "1.2.1", "fguillot/picodb" : "1.0.12", "fguillot/simpleLogger" : "1.0.1", - "fguillot/simple-validator" : "1.0.0", + "fguillot/simple-validator" : "1.0.1", "fguillot/simple-queue" : "1.0.1", - "paragonie/random_compat": "@stable", - "pimple/pimple" : "~3.0", - "ramsey/array_column": "@stable", - "swiftmailer/swiftmailer" : "~5.4", - "symfony/console" : "~2.7", - "symfony/event-dispatcher" : "~2.7", - "gregwar/captcha": "1.*" + "paragonie/random_compat": "2.0.2", + "pimple/pimple" : "3.0.2", + "ramsey/array_column": "1.1.3", + "swiftmailer/swiftmailer" : "5.4.2", + "symfony/console" : "2.8.7", + "symfony/event-dispatcher" : "2.7.14", + "gregwar/captcha": "1.1.1" }, "autoload" : { "classmap" : ["app/"], @@ -50,9 +50,10 @@ ] }, "require-dev" : { - "symfony/yaml" : "2.1", - "symfony/stopwatch" : "~2.6", - "phpunit/phpunit" : "4.8.*", - "phpunit/phpunit-selenium": "^2.0" + "phpdocumentor/reflection-docblock": "2.0.4", + "symfony/yaml": "2.8.7", + "symfony/stopwatch" : "2.6.13", + "phpunit/phpunit" : "4.8.26", + "phpunit/phpunit-selenium": "2.0.2" } } diff --git a/composer.lock b/composer.lock index e0177ed5..03c5e523 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "2de2026649db7bc41653bef80f974c6a", - "content-hash": "ea8ef74f1f1cf53b9f96b7609d756873", + "hash": "ab5b2c960b3a6d9f93883606269085e0", + "content-hash": "bd5f17c3382d7f85e33a68023927704c", "packages": [ { "name": "christian-riesen/base32", @@ -63,22 +63,25 @@ }, { "name": "christian-riesen/otp", - "version": "1.4", + "version": "1.4.3", "source": { "type": "git", "url": "https://github.com/ChristianRiesen/otp.git", - "reference": "a209b8bbd975d96d6b5287f8658562061adef1f8" + "reference": "20a539ce6280eb029030f4e7caefd5709a75e1ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ChristianRiesen/otp/zipball/a209b8bbd975d96d6b5287f8658562061adef1f8", - "reference": "a209b8bbd975d96d6b5287f8658562061adef1f8", + "url": "https://api.github.com/repos/ChristianRiesen/otp/zipball/20a539ce6280eb029030f4e7caefd5709a75e1ad", + "reference": "20a539ce6280eb029030f4e7caefd5709a75e1ad", "shasum": "" }, "require": { "christian-riesen/base32": ">=1.0", "php": ">=5.3.0" }, + "suggest": { + "paragonie/random_compat": "Optional polyfill for a more secure random generator for pre PHP7 versions" + }, "type": "library", "autoload": { "psr-0": { @@ -107,20 +110,20 @@ "rfc6238", "totp" ], - "time": "2015-02-12 09:11:49" + "time": "2015-10-08 08:17:59" }, { "name": "eluceo/ical", - "version": "0.8.0", + "version": "0.10.1", "source": { "type": "git", "url": "https://github.com/markuspoerschke/iCal.git", - "reference": "a291711851d1538e2726ffe95862aa5e340ddb9a" + "reference": "2dd99c12c0aa961c541380ab0c113135e14af33e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/markuspoerschke/iCal/zipball/a291711851d1538e2726ffe95862aa5e340ddb9a", - "reference": "a291711851d1538e2726ffe95862aa5e340ddb9a", + "url": "https://api.github.com/repos/markuspoerschke/iCal/zipball/2dd99c12c0aa961c541380ab0c113135e14af33e", + "reference": "2dd99c12c0aa961c541380ab0c113135e14af33e", "shasum": "" }, "require": { @@ -160,7 +163,7 @@ "ics", "php calendar" ], - "time": "2015-07-12 18:19:30" + "time": "2016-06-09 09:08:55" }, { "name": "erusev/parsedown", @@ -203,16 +206,16 @@ }, { "name": "fguillot/json-rpc", - "version": "v1.2.0", + "version": "v1.2.1", "source": { "type": "git", "url": "https://github.com/fguillot/JsonRPC.git", - "reference": "b002320b10aa1eeb7aee83f7b703cd6a6e99ff78" + "reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/b002320b10aa1eeb7aee83f7b703cd6a6e99ff78", - "reference": "b002320b10aa1eeb7aee83f7b703cd6a6e99ff78", + "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/d491bb549bfa11aff4c37abcea2ffb28c9523f69", + "reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69", "shasum": "" }, "require": { @@ -238,7 +241,7 @@ ], "description": "Simple Json-RPC client/server library that just works", "homepage": "https://github.com/fguillot/JsonRPC", - "time": "2016-05-29 13:06:36" + "time": "2016-06-25 23:11:10" }, { "name": "fguillot/picodb", @@ -331,16 +334,16 @@ }, { "name": "fguillot/simple-validator", - "version": "1.0.0", + "version": "v1.0.1", "source": { "type": "git", "url": "https://github.com/fguillot/simpleValidator.git", - "reference": "9579993f3dd0f03053b28fec1e7b9990edc3947b" + "reference": "23b0a99c5f11ad74d05f8845feaafbcfd9223eda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/9579993f3dd0f03053b28fec1e7b9990edc3947b", - "reference": "9579993f3dd0f03053b28fec1e7b9990edc3947b", + "url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/23b0a99c5f11ad74d05f8845feaafbcfd9223eda", + "reference": "23b0a99c5f11ad74d05f8845feaafbcfd9223eda", "shasum": "" }, "require": { @@ -363,7 +366,7 @@ ], "description": "Simple validator library", "homepage": "https://github.com/fguillot/simpleValidator", - "time": "2015-08-29 00:44:37" + "time": "2016-06-26 15:09:26" }, { "name": "fguillot/simpleLogger", @@ -682,16 +685,16 @@ }, { "name": "symfony/console", - "version": "v2.8.6", + "version": "v2.8.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "48221d3de4dc22d2cd57c97e8b9361821da86609" + "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/48221d3de4dc22d2cd57c97e8b9361821da86609", - "reference": "48221d3de4dc22d2cd57c97e8b9361821da86609", + "url": "https://api.github.com/repos/symfony/console/zipball/5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", + "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", "shasum": "" }, "require": { @@ -738,20 +741,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2016-04-26 12:00:47" + "time": "2016-06-06 15:06:25" }, { "name": "symfony/event-dispatcher", - "version": "v2.8.6", + "version": "v2.7.14", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "a158f13992a3147d466af7a23b564ac719a4ddd8" + "reference": "d3e09ed1224503791f31b913d22196f65f9afed5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a158f13992a3147d466af7a23b564ac719a4ddd8", - "reference": "a158f13992a3147d466af7a23b564ac719a4ddd8", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d3e09ed1224503791f31b913d22196f65f9afed5", + "reference": "d3e09ed1224503791f31b913d22196f65f9afed5", "shasum": "" }, "require": { @@ -759,10 +762,10 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5|~3.0.0", - "symfony/dependency-injection": "~2.6|~3.0.0", - "symfony/expression-language": "~2.6|~3.0.0", - "symfony/stopwatch": "~2.3|~3.0.0" + "symfony/config": "~2.0,>=2.0.5", + "symfony/dependency-injection": "~2.6", + "symfony/expression-language": "~2.6", + "symfony/stopwatch": "~2.3" }, "suggest": { "symfony/dependency-injection": "", @@ -771,7 +774,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "2.7-dev" } }, "autoload": { @@ -798,7 +801,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2016-05-03 18:59:18" + "time": "2016-06-06 11:03:51" }, { "name": "symfony/polyfill-mbstring", @@ -966,32 +969,32 @@ }, { "name": "phpspec/prophecy", - "version": "v1.6.0", + "version": "v1.6.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972" + "reference": "58a8137754bc24b25740d4281399a4a3596058e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972", - "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", + "reference": "58a8137754bc24b25740d4281399a4a3596058e0", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "~2.0", - "sebastian/comparator": "~1.1", - "sebastian/recursion-context": "~1.0" + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1", + "sebastian/recursion-context": "^1.0" }, "require-dev": { - "phpspec/phpspec": "~2.0" + "phpspec/phpspec": "^2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.5.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -1024,7 +1027,7 @@ "spy", "stub" ], - "time": "2016-02-15 07:46:21" + "time": "2016-06-07 08:13:47" }, { "name": "phpunit/php-code-coverage", @@ -1629,16 +1632,16 @@ }, { "name": "sebastian/exporter", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e" + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", "shasum": "" }, "require": { @@ -1646,12 +1649,13 @@ "sebastian/recursion-context": "~1.0" }, "require-dev": { + "ext-mbstring": "*", "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { @@ -1691,7 +1695,7 @@ "export", "exporter" ], - "time": "2015-06-21 07:55:53" + "time": "2016-06-17 09:04:28" }, { "name": "sebastian/global-state", @@ -1834,34 +1838,35 @@ }, { "name": "symfony/stopwatch", - "version": "v2.8.6", + "version": "v2.6.13", + "target-dir": "Symfony/Component/Stopwatch", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "9e24824b2a9a16e17ab997f61d70bc03948e434e" + "reference": "a0d91f2f4e2c60bd78f13388aa68f9d7cab8c987" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/9e24824b2a9a16e17ab997f61d70bc03948e434e", - "reference": "9e24824b2a9a16e17ab997f61d70bc03948e434e", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/a0d91f2f4e2c60bd78f13388aa68f9d7cab8c987", + "reference": "a0d91f2f4e2c60bd78f13388aa68f9d7cab8c987", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev" + "dev-master": "2.6-dev" } }, "autoload": { - "psr-4": { + "psr-0": { "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1879,36 +1884,38 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2016-03-04 07:54:35" + "time": "2015-07-01 18:23:01" }, { "name": "symfony/yaml", - "version": "v2.1.0", - "target-dir": "Symfony/Component/Yaml", + "version": "v2.8.7", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "f18e004fc975707bb4695df1dbbe9b0d8c8b7715" + "reference": "815fabf3f48c7d1df345a69d1ad1a88f59757b34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/f18e004fc975707bb4695df1dbbe9b0d8c8b7715", - "reference": "f18e004fc975707bb4695df1dbbe9b0d8c8b7715", + "url": "https://api.github.com/repos/symfony/yaml/zipball/815fabf3f48c7d1df345a69d1ad1a88f59757b34", + "reference": "815fabf3f48c7d1df345a69d1ad1a88f59757b34", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.3.9" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "2.8-dev" } }, "autoload": { - "psr-0": { - "Symfony\\Component\\Yaml": "" - } + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1916,29 +1923,26 @@ ], "authors": [ { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - }, - { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony Yaml Component", - "homepage": "http://symfony.com", - "time": "2012-08-22 13:48:41" + "homepage": "https://symfony.com", + "time": "2016-06-06 11:11:27" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "paragonie/random_compat": 0, - "ramsey/array_column": 0 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.3.3", + "php": ">=5.3.9", "ext-gd": "*", "ext-mbstring": "*", "ext-hash": "*", diff --git a/doc/api-authentication.markdown b/doc/api-authentication.markdown index 962e5b1b..3ba1e8f5 100644 --- a/doc/api-authentication.markdown +++ b/doc/api-authentication.markdown @@ -1,48 +1,26 @@ API Authentication ================== -Default method (HTTP Basic) ---------------------------- +API endpoint +------------ + +URL: `https://YOUR_SERVER/jsonrpc.php` -The API credentials are available on the settings page. -- API end-point: `https://YOUR_SERVER/jsonrpc.php` +Default method (HTTP Basic) +--------------------------- -If you want to use the "application api": +### Application credentials - Username: `jsonrpc` - Password: API token on the settings page -Otherwise for the "user api", just use the real username/passsword. +### User credentials + +- Use the real username and password The API use the [HTTP Basic Authentication Scheme described in the RFC2617](http://www.ietf.org/rfc/rfc2617.txt). -If there is an authentication error, you will receive the HTTP status code `401 Not Authorized`. - -### Authorized User API procedures - -- getMe -- getMyDashboard -- getMyActivityStream -- createMyPrivateProject -- getMyProjectsList -- getMyProjects -- getTimezone -- getVersion -- getDefaultTaskColor -- getDefaultTaskColors -- getColorList -- getProjectById -- getTask -- getTaskByReference -- getAllTasks -- openTask -- closeTask -- moveTaskPosition -- createTask -- updateTask -- getBoard -- getProjectActivity -- getMyOverdueTasks + Custom HTTP header ------------------ @@ -64,3 +42,14 @@ curl \ -d '{"jsonrpc": "2.0", "method": "getAllProjects", "id": 1}' \ http://localhost/kanboard/jsonrpc.php ``` + +Authentication error +-------------------- + +If the credentials are wrong, you will receive a `401 Not Authorized` and the corresponding JSON response. + + +Authorization error +------------------- + +If the connected user is not allowed to access to the resource, you will receive a `403 Forbidden`. diff --git a/doc/api-json-rpc.markdown b/doc/api-json-rpc.markdown index bb14b008..0f922a7c 100644 --- a/doc/api-json-rpc.markdown +++ b/doc/api-json-rpc.markdown @@ -8,25 +8,25 @@ There are two types of API access: ### Application API -- Access to the API with the user "jsonrpc" and the token available in settings +- Access to the API with the user "jsonrpc" and the token available on the settings page - Access to all procedures - No permission checked - There is no user session on the server +- No access to procedures that starts with "My..." (example: "getMe" or "getMyProjects") - Example of possible clients: tools to migrate/import data, create tasks from another system, etc... ### User API - Access to the API with the user credentials (username and password) -- Access to a restricted set of procedures -- The project permissions are checked +- Application role and project permissions are checked for each procedure - A user session is created on the server -- Example of possible clients: mobile/desktop application, command line utility, etc... +- Example of possible clients: native mobile/desktop application, command line utility, etc... Security -------- -- Always use HTTPS with a valid certificate -- If you make a mobile application, it's your job to store securely the user credentials on the device +- Always use HTTPS with a valid certificate (avoid clear text communication) +- If you make a mobile application, it's your responsability to store securely the user credentials on the device - After 3 authentication failure on the user api, the end-user have to unlock his account by using the login form - Two factor authentication is not yet available through the API diff --git a/doc/api-project-permission-procedures.markdown b/doc/api-project-permission-procedures.markdown index 2844ae3c..d5e9b066 100644 --- a/doc/api-project-permission-procedures.markdown +++ b/doc/api-project-permission-procedures.markdown @@ -272,3 +272,36 @@ Response example: "result": true } ``` + +## getProjectUserRole + +- Purpose: **Get the role of a user for a given project** +- Parameters: + - **project_id** (integer, required) + - **user_id** (integer, required) +- Result on success: **role name** +- Result on failure: **false** + +Request example: + +```json +{ + "jsonrpc": "2.0", + "method": "getProjectUserRole", + "id": 2114673298, + "params": [ + "2", + "3" + ] +} +``` + +Response example: + +```json +{ + "jsonrpc": "2.0", + "id": 2114673298, + "result": "project-viewer" +} +``` diff --git a/doc/api-project-procedures.markdown b/doc/api-project-procedures.markdown index 6cc1b15b..d375852c 100644 --- a/doc/api-project-procedures.markdown +++ b/doc/api-project-procedures.markdown @@ -7,6 +7,8 @@ API Project Procedures - Parameters: - **name** (string, required) - **description** (string, optional) + - **owner_id** (integer, optional) + - **identifier** (string, optional) - Result on success: **project_id** - Result on failure: **false** @@ -183,9 +185,11 @@ Response example: - Purpose: **Update a project** - Parameters: - - **id** (integer, required) - - **name** (string, required) + - **project_id** (integer, required) + - **name** (string, optional) - **description** (string, optional) + - **owner_id** (integer, optional) + - **identifier** (string, optional) - Result on success: **true** - Result on failure: **false** @@ -197,7 +201,7 @@ Request example: "method": "updateProject", "id": 1853996288, "params": { - "id": 1, + "project_id": 1, "name": "PHP client update" } } diff --git a/doc/requirements.markdown b/doc/requirements.markdown index 9943465a..f6c9b309 100644 --- a/doc/requirements.markdown +++ b/doc/requirements.markdown @@ -51,7 +51,7 @@ Kanboard is pre-configured to work with Apache (URL rewriting). | PHP Version | |----------------| -| PHP >= 5.3.3 | +| PHP >= 5.3.9 | | PHP 5.4 | | PHP 5.5 | | PHP 5.6 | diff --git a/doc/tests.markdown b/doc/tests.markdown index 5e3d71d2..59177f87 100644 --- a/doc/tests.markdown +++ b/doc/tests.markdown @@ -9,7 +9,7 @@ Requirements ------------ - Linux/Unix machine -- PHP cli +- PHP - PHPUnit installed - Mysql and Postgresql (optional) - Selenium (optional) @@ -85,46 +85,37 @@ From your Kanboard directory, run the command `phpunit -c tests/units.postgres.x Integration Tests ----------------- -Acceptance tests (also known as end-to-end tests and sometimes functional tests) allow us to test the actual functionality of the browser using Selenium and PHPUnit. +Integration tests are mainly used to test the API. +The test suites are making real HTTP calls to the application that run inside a container. -The PHPUnit config file is `tests/acceptance.xml`. -From your Kanboard directory, run the command `phpunit -c tests/units.sqlite.xml`. - -Actually only the API calls are tested. - -Real HTTP calls are made with those tests. -So a local instance of Kanboard is necessary and must listen on `http://localhost:8000/`. - -All data will be removed/altered by the test suite. -Moreover the script will reset and set a new API key. +### Requirements -1. Start a local instance of Kanboard `php -S 127.0.0.1:8000` -2. Run the test suite from another terminal +- PHP +- Composer +- Unix operating system (Mac OS or Linux) +- Docker +- Docker Compose -The same method as above is used to run tests across different databases: +### Running integration tests -- Sqlite: `phpunit -c tests/integration.sqlite.xml` -- Mysql: `phpunit -c tests/integration.mysql.xml` -- Postgresql: `phpunit -c tests/integration.postgres.xml` +Integration tests are using Docker containers. +There are 3 different environment available to run tests against each supported database. -Example: +You can use these commands to run each test suite: ```bash -phpunit -c tests/integration.sqlite.xml - -PHPUnit 5.0.0 by Sebastian Bergmann and contributors. +# Run tests with Sqlite +make integration-test-sqlite -............................................................... 63 / 135 ( 46%) -............................................................... 126 / 135 ( 93%) -......... 135 / 135 (100%) +# Run tests with Mysql +make integration-test-mysql -Time: 1.18 minutes, Memory: 14.75Mb - -OK (135 tests, 526 assertions) +# Run tests with Postgres +make integration-test-postgres ``` Acceptance Tests ------------------ +---------------- Acceptance tests (also sometimes known as end-to-end tests, and functional tests) test the actual functionality of the UI in a browser using Selenium. diff --git a/tests/configs/config.mysql.php b/tests/configs/config.mysql.php new file mode 100644 index 00000000..27e32744 --- /dev/null +++ b/tests/configs/config.mysql.php @@ -0,0 +1,12 @@ +<?php + +define('DB_DRIVER', 'mysql'); +define('DB_USERNAME', 'root'); +define('DB_PASSWORD', 'kanboard'); +define('DB_HOSTNAME', 'mysql'); +define('DB_NAME', 'kanboard'); + +define('DEBUG', true); +define('LOG_DRIVER', 'stderr'); + +define('API_AUTHENTICATION_TOKEN', 'test'); diff --git a/tests/configs/config.postgres.php b/tests/configs/config.postgres.php new file mode 100644 index 00000000..161a2349 --- /dev/null +++ b/tests/configs/config.postgres.php @@ -0,0 +1,12 @@ +<?php + +define('DB_DRIVER', 'postgres'); +define('DB_USERNAME', 'postgres'); +define('DB_PASSWORD', 'postgres'); +define('DB_HOSTNAME', 'postgres'); +define('DB_NAME', 'kanboard'); + +define('DEBUG', true); +define('LOG_DRIVER', 'stderr'); + +define('API_AUTHENTICATION_TOKEN', 'test'); diff --git a/tests/configs/config.sqlite.php b/tests/configs/config.sqlite.php new file mode 100644 index 00000000..e969c17c --- /dev/null +++ b/tests/configs/config.sqlite.php @@ -0,0 +1,8 @@ +<?php + +define('DB_DRIVER', 'sqlite'); + +define('DEBUG', true); +define('LOG_DRIVER', 'file'); + +define('API_AUTHENTICATION_TOKEN', 'test'); diff --git a/tests/docker/Dockerfile.xenial b/tests/docker/Dockerfile.xenial new file mode 100644 index 00000000..a48d0525 --- /dev/null +++ b/tests/docker/Dockerfile.xenial @@ -0,0 +1,24 @@ +FROM ubuntu:16.04 + +RUN mkdir -p /var/lock/apache2 /var/run/apache2 /var/log/supervisor + +RUN apt-get update -qq && \ + apt-get install -y apache2 supervisor cron curl unzip \ + libapache2-mod-php7.0 php7.0-cli php7.0-mbstring php7.0-xml php7.0-mysql php7.0-sqlite3 \ + php7.0-opcache php7.0-json php7.0-pgsql php7.0-ldap php7.0-gd php7.0-zip && \ + apt clean && \ + echo "ServerName localhost" >> /etc/apache2/apache2.conf && \ + sed -ri 's/AllowOverride None/AllowOverride All/g' /etc/apache2/apache2.conf && \ + a2enmod rewrite && \ + curl -sS https://getcomposer.org/installer | php -- --filename=/usr/local/bin/composer + +COPY . /var/www/html + +RUN chown -R www-data:www-data /var/www/html/data /var/www/html/plugins + +COPY tests/docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY tests/configs /configs/ + +EXPOSE 80 + +ENTRYPOINT ["/var/www/html/tests/docker/entrypoint.sh"] diff --git a/tests/docker/compose.integration.mysql.yaml b/tests/docker/compose.integration.mysql.yaml new file mode 100644 index 00000000..6eda5eec --- /dev/null +++ b/tests/docker/compose.integration.mysql.yaml @@ -0,0 +1,27 @@ +version: '2' +services: + mysql: + image: mysql:5.7 + environment: + MYSQL_ROOT_PASSWORD: "kanboard" + MYSQL_DATABASE: "kanboard" + MYSQL_USER: "kanboard" + MYSQL_PASSWORD: "kanboard" + ports: + - "3306:3306" + app: + build: + context: ../.. + dockerfile: tests/docker/Dockerfile.xenial + ports: + - "8000:80" + depends_on: + - mysql + command: config-mysql + tests: + build: + context: ../.. + dockerfile: tests/docker/Dockerfile.xenial + depends_on: + - app + command: integration-test-mysql diff --git a/tests/docker/compose.integration.postgres.yaml b/tests/docker/compose.integration.postgres.yaml new file mode 100644 index 00000000..ed095248 --- /dev/null +++ b/tests/docker/compose.integration.postgres.yaml @@ -0,0 +1,26 @@ +version: '2' +services: + postgres: + image: postgres:9.5 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: kanboard + ports: + - "5432:5432" + app: + build: + context: ../.. + dockerfile: tests/docker/Dockerfile.xenial + ports: + - "8000:80" + depends_on: + - postgres + command: config-postgres + tests: + build: + context: ../.. + dockerfile: tests/docker/Dockerfile.xenial + depends_on: + - app + command: integration-test-postgres diff --git a/tests/docker/compose.integration.sqlite.yaml b/tests/docker/compose.integration.sqlite.yaml new file mode 100644 index 00000000..6431484e --- /dev/null +++ b/tests/docker/compose.integration.sqlite.yaml @@ -0,0 +1,16 @@ +version: '2' +services: + app: + build: + context: ../.. + dockerfile: tests/docker/Dockerfile.xenial + ports: + - "8000:80" + command: config-sqlite + tests: + build: + context: ../.. + dockerfile: tests/docker/Dockerfile.xenial + depends_on: + - app + command: integration-test-sqlite diff --git a/tests/docker/entrypoint.sh b/tests/docker/entrypoint.sh new file mode 100755 index 00000000..a88c7ed8 --- /dev/null +++ b/tests/docker/entrypoint.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +function wait_schema_creation() { + curl -s http://app/login > /dev/null + sleep $1 +} + +case "$1" in +"config-sqlite") + cp /configs/config.sqlite.php /var/www/html/config.php + /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf + ;; +"config-postgres") + cp /configs/config.postgres.php /var/www/html/config.php + /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf + ;; +"config-mysql") + cp /configs/config.mysql.php /var/www/html/config.php + /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf + ;; +"integration-test-sqlite") + wait_schema_creation 1 + /var/www/html/vendor/phpunit/phpunit/phpunit -c /var/www/html/tests/integration.sqlite.xml + ;; +"integration-test-postgres") + wait_schema_creation 5 + /var/www/html/vendor/phpunit/phpunit/phpunit -c /var/www/html/tests/integration.postgres.xml + ;; +"integration-test-mysql") + wait_schema_creation 15 + /var/www/html/vendor/phpunit/phpunit/phpunit -c /var/www/html/tests/integration.mysql.xml + ;; +esac diff --git a/tests/docker/supervisord.conf b/tests/docker/supervisord.conf new file mode 100644 index 00000000..4d5ee621 --- /dev/null +++ b/tests/docker/supervisord.conf @@ -0,0 +1,6 @@ +[supervisord] +nodaemon=true + +[program:apache2] +command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2 -DFOREGROUND" +autorestart=true diff --git a/tests/integration.mysql.xml b/tests/integration.mysql.xml index 9d87f77e..33813187 100644 --- a/tests/integration.mysql.xml +++ b/tests/integration.mysql.xml @@ -5,16 +5,8 @@ </testsuite> </testsuites> <php> - <const name="API_URL" value="http://localhost:8000/jsonrpc.php" /> - <const name="API_KEY" value="19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929" /> - <const name="DB_DRIVER" value="mysql" /> - <const name="DB_NAME" value="kanboard" /> - <const name="DB_HOSTNAME" value="localhost" /> - <const name="DB_USERNAME" value="root" /> - <const name="DB_PASSWORD" value="" /> - <const name="DB_PORT" value="" /> - <const name="DB_SSL_KEY" value="" /> - <const name="DB_SSL_CA" value="" /> - <const name="DB_SSL_CERT" value="" /> + <const name="BASE_URL" value="http://app/" /> + <const name="API_URL" value="http://app/jsonrpc.php" /> + <const name="API_KEY" value="test" /> </php> </phpunit> diff --git a/tests/integration.postgres.xml b/tests/integration.postgres.xml index ed8a3de3..33813187 100644 --- a/tests/integration.postgres.xml +++ b/tests/integration.postgres.xml @@ -5,13 +5,8 @@ </testsuite> </testsuites> <php> - <const name="API_URL" value="http://localhost:8000/jsonrpc.php" /> - <const name="API_KEY" value="19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929" /> - <const name="DB_DRIVER" value="postgres" /> - <const name="DB_NAME" value="kanboard" /> - <const name="DB_HOSTNAME" value="localhost" /> - <const name="DB_USERNAME" value="postgres" /> - <const name="DB_PASSWORD" value="postgres" /> - <const name="DB_PORT" value="" /> + <const name="BASE_URL" value="http://app/" /> + <const name="API_URL" value="http://app/jsonrpc.php" /> + <const name="API_KEY" value="test" /> </php> -</phpunit>
\ No newline at end of file +</phpunit> diff --git a/tests/integration.sqlite.xml b/tests/integration.sqlite.xml index 1964f822..33813187 100644 --- a/tests/integration.sqlite.xml +++ b/tests/integration.sqlite.xml @@ -5,9 +5,8 @@ </testsuite> </testsuites> <php> - <const name="API_URL" value="http://127.0.0.1:8000/jsonrpc.php" /> - <const name="API_KEY" value="19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929" /> - <const name="DB_DRIVER" value="sqlite" /> - <const name="DB_FILENAME" value="data/db.sqlite" /> + <const name="BASE_URL" value="http://app/" /> + <const name="API_URL" value="http://app/jsonrpc.php" /> + <const name="API_KEY" value="test" /> </php> -</phpunit>
\ No newline at end of file +</phpunit> diff --git a/tests/integration/ActionProcedureTest.php b/tests/integration/ActionProcedureTest.php new file mode 100644 index 00000000..432de3d3 --- /dev/null +++ b/tests/integration/ActionProcedureTest.php @@ -0,0 +1,66 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class ActionProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test actions'; + + public function testGetAvailableActions() + { + $actions = $this->app->getAvailableActions(); + $this->assertNotEmpty($actions); + $this->assertInternalType('array', $actions); + $this->assertArrayHasKey('\Kanboard\Action\TaskCloseColumn', $actions); + } + + public function testGetAvailableActionEvents() + { + $events = $this->app->getAvailableActionEvents(); + $this->assertNotEmpty($events); + $this->assertInternalType('array', $events); + $this->assertArrayHasKey('task.move.column', $events); + } + + public function testGetCompatibleActionEvents() + { + $events = $this->app->getCompatibleActionEvents('\Kanboard\Action\TaskCloseColumn'); + $this->assertNotEmpty($events); + $this->assertInternalType('array', $events); + $this->assertArrayHasKey('task.move.column', $events); + } + + public function testCRUD() + { + $this->assertCreateTeamProject(); + $this->assertCreateAction(); + $this->assertGetActions(); + $this->assertRemoveAction(); + } + + public function assertCreateAction() + { + $actionId = $this->app->createAction($this->projectId, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1)); + $this->assertNotFalse($actionId); + $this->assertTrue($actionId > 0); + } + + public function assertGetActions() + { + $actions = $this->app->getActions($this->projectId); + $this->assertNotEmpty($actions); + $this->assertInternalType('array', $actions); + $this->assertArrayHasKey('id', $actions[0]); + $this->assertArrayHasKey('project_id', $actions[0]); + $this->assertArrayHasKey('event_name', $actions[0]); + $this->assertArrayHasKey('action_name', $actions[0]); + $this->assertArrayHasKey('params', $actions[0]); + $this->assertArrayHasKey('column_id', $actions[0]['params']); + } + + public function assertRemoveAction() + { + $actionId = $this->app->createAction($this->projectId, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1)); + $this->assertTrue($this->app->removeAction($actionId)); + } +} diff --git a/tests/integration/ApiTest.php b/tests/integration/ApiTest.php deleted file mode 100644 index f552bea9..00000000 --- a/tests/integration/ApiTest.php +++ /dev/null @@ -1,928 +0,0 @@ -<?php - -require_once __DIR__.'/../../vendor/autoload.php'; - -class Api extends PHPUnit_Framework_TestCase -{ - private $client = null; - - public static function setUpBeforeClass() - { - if (DB_DRIVER === 'sqlite') { - @unlink(DB_FILENAME); - } elseif (DB_DRIVER === 'mysql') { - $pdo = new PDO('mysql:host='.DB_HOSTNAME, DB_USERNAME, DB_PASSWORD); - $pdo->exec('DROP DATABASE '.DB_NAME); - $pdo->exec('CREATE DATABASE '.DB_NAME); - $pdo = null; - } elseif (DB_DRIVER === 'postgres') { - $pdo = new PDO('pgsql:host='.DB_HOSTNAME, DB_USERNAME, DB_PASSWORD); - $pdo->exec('DROP DATABASE '.DB_NAME); - $pdo->exec('CREATE DATABASE '.DB_NAME.' WITH OWNER '.DB_USERNAME); - $pdo = null; - } - - $service = new Kanboard\ServiceProvider\DatabaseProvider; - - $db = $service->getInstance(); - $db->table('settings')->eq('option', 'api_token')->update(array('value' => API_KEY)); - $db->table('settings')->eq('option', 'application_timezone')->update(array('value' => 'Europe/Paris')); - $db->closeConnection(); - } - - public function setUp() - { - $this->client = new JsonRPC\Client(API_URL); - $this->client->authentication('jsonrpc', API_KEY); - // $this->client->debug = true; - } - - private function getTaskId() - { - $tasks = $this->client->getAllTasks(1, 1); - $this->assertNotEmpty($tasks); - - return $tasks[0]['id']; - } - - public function testRemoveAll() - { - $projects = $this->client->getAllProjects(); - - if ($projects) { - foreach ($projects as $project) { - $this->assertEquals('http://127.0.0.1:8000/?controller=BoardViewController&action=show&project_id='.$project['id'], $project['url']['board']); - $this->assertEquals('http://127.0.0.1:8000/?controller=CalendarController&action=show&project_id='.$project['id'], $project['url']['calendar']); - $this->assertEquals('http://127.0.0.1:8000/?controller=TaskListController&action=show&project_id='.$project['id'], $project['url']['list']); - $this->assertTrue($this->client->removeProject($project['id'])); - } - } - } - - public function testCreateProject() - { - $project_id = $this->client->createProject('API test'); - $this->assertNotFalse($project_id); - $this->assertInternalType('int', $project_id); - } - - public function testGetProjectById() - { - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertEquals(1, $project['id']); - $this->assertEquals('http://127.0.0.1:8000/?controller=BoardViewController&action=show&project_id='.$project['id'], $project['url']['board']); - $this->assertEquals('http://127.0.0.1:8000/?controller=CalendarController&action=show&project_id='.$project['id'], $project['url']['calendar']); - $this->assertEquals('http://127.0.0.1:8000/?controller=TaskListController&action=show&project_id='.$project['id'], $project['url']['list']); - } - - public function testGetProjectByName() - { - $project = $this->client->getProjectByName('API test'); - $this->assertNotEmpty($project); - $this->assertEquals(1, $project['id']); - $this->assertEquals('http://127.0.0.1:8000/?controller=BoardViewController&action=show&project_id='.$project['id'], $project['url']['board']); - $this->assertEquals('http://127.0.0.1:8000/?controller=CalendarController&action=show&project_id='.$project['id'], $project['url']['calendar']); - $this->assertEquals('http://127.0.0.1:8000/?controller=TaskListController&action=show&project_id='.$project['id'], $project['url']['list']); - - $project = $this->client->getProjectByName(array('name' => 'API test')); - $this->assertNotEmpty($project); - $this->assertEquals(1, $project['id']); - - $project = $this->client->getProjectByName('None'); - $this->assertEmpty($project); - $this->assertNull($project); - } - - public function testGetAllProjects() - { - $projects = $this->client->getAllProjects(); - $this->assertNotEmpty($projects); - - foreach ($projects as $project) { - $this->assertEquals('http://127.0.0.1:8000/?controller=BoardViewController&action=show&project_id='.$project['id'], $project['url']['board']); - $this->assertEquals('http://127.0.0.1:8000/?controller=CalendarController&action=show&project_id='.$project['id'], $project['url']['calendar']); - $this->assertEquals('http://127.0.0.1:8000/?controller=TaskListController&action=show&project_id='.$project['id'], $project['url']['list']); - } - } - - public function testUpdateProject() - { - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertTrue($this->client->execute('updateProject', array('id' => 1, 'name' => 'API test 2'))); - - $project = $this->client->getProjectById(1); - $this->assertEquals('API test 2', $project['name']); - - $this->assertTrue($this->client->execute('updateProject', array('id' => 1, 'name' => 'API test', 'description' => 'test'))); - - $project = $this->client->getProjectById(1); - $this->assertEquals('API test', $project['name']); - $this->assertEquals('test', $project['description']); - } - - public function testDisableProject() - { - $this->assertTrue($this->client->disableProject(1)); - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertEquals(0, $project['is_active']); - } - - public function testEnableProject() - { - $this->assertTrue($this->client->enableProject(1)); - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertEquals(1, $project['is_active']); - } - - public function testEnableProjectPublicAccess() - { - $this->assertTrue($this->client->enableProjectPublicAccess(1)); - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertEquals(1, $project['is_public']); - $this->assertNotEmpty($project['token']); - } - - public function testDisableProjectPublicAccess() - { - $this->assertTrue($this->client->disableProjectPublicAccess(1)); - $project = $this->client->getProjectById(1); - $this->assertNotEmpty($project); - $this->assertEquals(0, $project['is_public']); - $this->assertEmpty($project['token']); - } - - public function testgetProjectActivities() - { - $activities = $this->client->getProjectActivities(array('project_ids' => array(1))); - $this->assertInternalType('array', $activities); - $this->assertCount(0, $activities); - } - - public function testgetProjectActivity() - { - $activities = $this->client->getProjectActivity(1); - $this->assertInternalType('array', $activities); - $this->assertCount(0, $activities); - } - - public function testCreateTaskWithWrongMember() - { - $task = array( - 'title' => 'Task #1', - 'color_id' => 'blue', - 'owner_id' => 1, - 'project_id' => 1, - 'column_id' => 2, - ); - - $task_id = $this->client->createTask($task); - - $this->assertFalse($task_id); - } - - public function testGetAllowedUsers() - { - $users = $this->client->getMembers(1); - $this->assertNotFalse($users); - $this->assertEquals(array(), $users); - } - - public function testAddMember() - { - $this->assertTrue($this->client->allowUser(1, 1)); - } - - public function testCreateTask() - { - $task = array( - 'title' => 'Task #1', - 'color_id' => 'blue', - 'owner_id' => 1, - 'project_id' => 1, - 'column_id' => 2, - ); - - $task_id = $this->client->createTask($task); - - $this->assertNotFalse($task_id); - $this->assertInternalType('int', $task_id); - $this->assertTrue($task_id > 0); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testCreateTaskWithBadParams() - { - $task = array( - 'title' => 'Task #1', - 'color_id' => 'blue', - 'owner_id' => 1, - ); - - $this->client->createTask($task); - } - - public function testGetTask() - { - $task = $this->client->getTask(1); - - $this->assertNotFalse($task); - $this->assertTrue(is_array($task)); - $this->assertEquals('Task #1', $task['title']); - $this->assertEquals('http://127.0.0.1:8000/?controller=TaskViewController&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'], $task['url']); - } - - public function testGetAllTasks() - { - $tasks = $this->client->getAllTasks(1, 1); - - $this->assertNotFalse($tasks); - $this->assertTrue(is_array($tasks)); - $this->assertEquals('Task #1', $tasks[0]['title']); - $this->assertEquals('http://127.0.0.1:8000/?controller=TaskViewController&action=show&task_id='.$tasks[0]['id'].'&project_id='.$tasks[0]['project_id'], $tasks[0]['url']); - - $tasks = $this->client->getAllTasks(2, 0); - - $this->assertNotFalse($tasks); - $this->assertTrue(is_array($tasks)); - $this->assertEmpty($tasks); - } - - public function testMoveTaskSwimlane() - { - $task_id = $this->getTaskId(); - - $task = $this->client->getTask($task_id); - $this->assertNotFalse($task); - $this->assertTrue(is_array($task)); - $this->assertEquals(1, $task['position']); - $this->assertEquals(2, $task['column_id']); - $this->assertEquals(0, $task['swimlane_id']); - - $moved_timestamp = $task['date_moved']; - sleep(1); - $this->assertTrue($this->client->moveTaskPosition(1, $task_id, 4, 1, 2)); - - $task = $this->client->getTask($task_id); - $this->assertNotFalse($task); - $this->assertTrue(is_array($task)); - $this->assertEquals(1, $task['position']); - $this->assertEquals(4, $task['column_id']); - $this->assertEquals(2, $task['swimlane_id']); - $this->assertNotEquals($moved_timestamp, $task['date_moved']); - } - - public function testUpdateTask() - { - $task = $this->client->getTask(1); - - $values = array(); - $values['id'] = $task['id']; - $values['color_id'] = 'green'; - $values['description'] = 'test'; - $values['date_due'] = ''; - - $this->assertTrue($this->client->execute('updateTask', $values)); - } - - public function testRemoveTask() - { - $this->assertTrue($this->client->removeTask(1)); - } - - public function testRemoveUsers() - { - $users = $this->client->getAllUsers(); - $this->assertNotFalse($users); - $this->assertNotEmpty($users); - - foreach ($users as $user) { - if ($user['id'] > 1) { - $this->assertTrue($this->client->removeUser($user['id'])); - } - } - } - - public function testCreateUser() - { - $user = array( - 'username' => 'toto', - 'name' => 'Toto', - 'password' => '123456', - ); - - $user_id = $this->client->execute('createUser', $user); - $this->assertNotFalse($user_id); - $this->assertInternalType('int', $user_id); - $this->assertTrue($user_id > 0); - } - - public function testCreateManagerUser() - { - $user = array( - 'username' => 'manager', - 'name' => 'Manager', - 'password' => '123456', - 'role' => 'app-manager' - ); - - $user_id = $this->client->execute('createUser', $user); - $this->assertNotFalse($user_id); - $this->assertInternalType('int', $user_id); - $this->assertTrue($user_id > 0); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testCreateUserWithBadParams() - { - $user = array( - 'name' => 'Titi', - 'password' => '123456', - ); - - $this->assertNull($this->client->execute('createUser', $user)); - } - - public function testGetUser() - { - $user = $this->client->getUser(2); - $this->assertNotFalse($user); - $this->assertTrue(is_array($user)); - $this->assertEquals('toto', $user['username']); - - $user = $this->client->getUser(3); - $this->assertNotEmpty($user); - $this->assertEquals('app-manager', $user['role']); - - $this->assertNull($this->client->getUser(2222)); - } - - public function testGetUserByName() - { - $user = $this->client->getUserByName('toto'); - $this->assertNotFalse($user); - $this->assertTrue(is_array($user)); - $this->assertEquals(2, $user['id']); - - $user = $this->client->getUserByName('manager'); - $this->assertNotEmpty($user); - $this->assertEquals('app-manager', $user['role']); - - $this->assertNull($this->client->getUserByName('nonexistantusername')); - } - - public function testUpdateUser() - { - $user = array(); - $user['id'] = 2; - $user['username'] = 'titi'; - $user['name'] = 'Titi'; - - $this->assertTrue($this->client->execute('updateUser', $user)); - - $user = $this->client->getUser(2); - $this->assertNotFalse($user); - $this->assertTrue(is_array($user)); - $this->assertEquals('titi', $user['username']); - $this->assertEquals('Titi', $user['name']); - - $user = array(); - $user['id'] = 2; - $user['email'] = 'titi@localhost'; - - $this->assertTrue($this->client->execute('updateUser', $user)); - - $user = $this->client->getUser(2); - $this->assertNotFalse($user); - $this->assertTrue(is_array($user)); - $this->assertEquals('titi@localhost', $user['email']); - } - - public function testAllowedUser() - { - $this->assertTrue($this->client->allowUser(1, 2)); - - $users = $this->client->getMembers(1); - $this->assertNotFalse($users); - $this->assertEquals(array(1 => 'admin', 2 => 'Titi'), $users); - } - - public function testRevokeUser() - { - $this->assertTrue($this->client->revokeUser(1, 2)); - - $users = $this->client->getMembers(1); - $this->assertNotFalse($users); - $this->assertEquals(array(1 => 'admin'), $users); - } - - public function testCreateComment() - { - $task = array( - 'title' => 'Task with comment', - 'color_id' => 'red', - 'owner_id' => 1, - 'project_id' => 1, - 'column_id' => 1, - ); - - $this->assertNotFalse($this->client->execute('createTask', $task)); - - $tasks = $this->client->getAllTasks(1, 1); - $this->assertNotEmpty($tasks); - $this->assertEquals(1, count($tasks)); - - $comment = array( - 'task_id' => $tasks[0]['id'], - 'user_id' => 2, - 'content' => 'boo', - ); - - $comment_id = $this->client->execute('createComment', $comment); - - $this->assertNotFalse($comment_id); - $this->assertInternalType('int', $comment_id); - $this->assertTrue($comment_id > 0); - } - - public function testGetComment() - { - $comment = $this->client->getComment(1); - $this->assertNotFalse($comment); - $this->assertNotEmpty($comment); - $this->assertEquals(2, $comment['user_id']); - $this->assertEquals('boo', $comment['comment']); - } - - public function testUpdateComment() - { - $comment = array(); - $comment['id'] = 1; - $comment['content'] = 'test'; - - $this->assertTrue($this->client->execute('updateComment', $comment)); - - $comment = $this->client->getComment(1); - $this->assertEquals('test', $comment['comment']); - } - - public function testGetAllComments() - { - $task_id = $this->getTaskId(); - - $comment = array( - 'task_id' => $task_id, - 'user_id' => 1, - 'content' => 'blabla', - ); - - $comment_id = $this->client->createComment($comment); - - $this->assertNotFalse($comment_id); - $this->assertInternalType('int', $comment_id); - $this->assertTrue($comment_id > 0); - - $comments = $this->client->getAllComments($task_id); - $this->assertNotFalse($comments); - $this->assertNotEmpty($comments); - $this->assertTrue(is_array($comments)); - $this->assertEquals(2, count($comments)); - } - - public function testRemoveComment() - { - $task_id = $this->getTaskId(); - - $comments = $this->client->getAllComments($task_id); - $this->assertNotFalse($comments); - $this->assertNotEmpty($comments); - $this->assertTrue(is_array($comments)); - - foreach ($comments as $comment) { - $this->assertTrue($this->client->removeComment($comment['id'])); - } - - $comments = $this->client->getAllComments($task_id); - $this->assertNotFalse($comments); - $this->assertEmpty($comments); - $this->assertTrue(is_array($comments)); - } - - public function testCreateSubtask() - { - $subtask = array( - 'task_id' => $this->getTaskId(), - 'title' => 'subtask #1', - ); - - $subtask_id = $this->client->createSubtask($subtask); - - $this->assertNotFalse($subtask_id); - $this->assertInternalType('int', $subtask_id); - $this->assertTrue($subtask_id > 0); - } - - public function testGetSubtask() - { - $subtask = $this->client->getSubtask(1); - $this->assertNotFalse($subtask); - $this->assertNotEmpty($subtask); - $this->assertEquals($this->getTaskId(), $subtask['task_id']); - $this->assertEquals(0, $subtask['user_id']); - $this->assertEquals('subtask #1', $subtask['title']); - } - - public function testUpdateSubtask() - { - $subtask = array(); - $subtask['id'] = 1; - $subtask['task_id'] = $this->getTaskId(); - $subtask['title'] = 'test'; - - $this->assertTrue($this->client->execute('updateSubtask', $subtask)); - - $subtask = $this->client->getSubtask(1); - $this->assertEquals('test', $subtask['title']); - } - - public function testGetAllSubtasks() - { - $subtask = array( - 'task_id' => $this->getTaskId(), - 'user_id' => 2, - 'title' => 'Subtask #2', - ); - - $this->assertNotFalse($this->client->execute('createSubtask', $subtask)); - - $subtasks = $this->client->getAllSubtasks($this->getTaskId()); - $this->assertNotFalse($subtasks); - $this->assertNotEmpty($subtasks); - $this->assertTrue(is_array($subtasks)); - $this->assertEquals(2, count($subtasks)); - } - - public function testRemoveSubtask() - { - $this->assertTrue($this->client->removeSubtask(1)); - - $subtasks = $this->client->getAllSubtasks($this->getTaskId()); - $this->assertNotFalse($subtasks); - $this->assertNotEmpty($subtasks); - $this->assertTrue(is_array($subtasks)); - $this->assertEquals(1, count($subtasks)); - } - - public function testMoveTaskPosition() - { - $task_id = $this->getTaskId(); - $this->assertTrue($this->client->moveTaskPosition(1, $task_id, 3, 1)); - - $task = $this->client->getTask($task_id); - $this->assertNotFalse($task); - $this->assertTrue(is_array($task)); - $this->assertEquals(1, $task['position']); - $this->assertEquals(3, $task['column_id']); - } - - public function testCategoryCreation() - { - $category = array( - 'name' => 'Category', - 'project_id' => 1, - ); - - $cat_id = $this->client->execute('createCategory', $category); - $this->assertNotFalse($cat_id); - $this->assertInternalType('int', $cat_id); - $this->assertTrue($cat_id > 0); - - // Duplicate - - $category = array( - 'name' => 'Category', - 'project_id' => 1, - ); - - $this->assertFalse($this->client->execute('createCategory', $category)); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testCategoryCreationWithBadParams() - { - // Missing project id - $category = array( - 'name' => 'Category', - ); - - $this->assertNull($this->client->execute('createCategory', $category)); - } - - public function testCategoryRead() - { - $category = $this->client->getCategory(1); - - $this->assertTrue(is_array($category)); - $this->assertNotEmpty($category); - $this->assertEquals(1, $category['id']); - $this->assertEquals('Category', $category['name']); - $this->assertEquals(1, $category['project_id']); - } - - public function testGetAllCategories() - { - $categories = $this->client->getAllCategories(1); - - $this->assertNotEmpty($categories); - $this->assertNotFalse($categories); - $this->assertTrue(is_array($categories)); - $this->assertEquals(1, count($categories)); - $this->assertEquals(1, $categories[0]['id']); - $this->assertEquals('Category', $categories[0]['name']); - $this->assertEquals(1, $categories[0]['project_id']); - } - - public function testCategoryUpdate() - { - $category = array( - 'id' => 1, - 'name' => 'Renamed category', - ); - - $this->assertTrue($this->client->execute('updateCategory', $category)); - - $category = $this->client->getCategory(1); - $this->assertTrue(is_array($category)); - $this->assertNotEmpty($category); - $this->assertEquals(1, $category['id']); - $this->assertEquals('Renamed category', $category['name']); - $this->assertEquals(1, $category['project_id']); - } - - public function testCategoryRemove() - { - $this->assertTrue($this->client->removeCategory(1)); - $this->assertFalse($this->client->removeCategory(1)); - $this->assertFalse($this->client->removeCategory(1111)); - } - - public function testGetAvailableActions() - { - $actions = $this->client->getAvailableActions(); - $this->assertNotEmpty($actions); - $this->assertInternalType('array', $actions); - $this->assertArrayHasKey('\Kanboard\Action\TaskCloseColumn', $actions); - } - - public function testGetAvailableActionEvents() - { - $events = $this->client->getAvailableActionEvents(); - $this->assertNotEmpty($events); - $this->assertInternalType('array', $events); - $this->assertArrayHasKey('task.move.column', $events); - } - - public function testGetCompatibleActionEvents() - { - $events = $this->client->getCompatibleActionEvents('\Kanboard\Action\TaskCloseColumn'); - $this->assertNotEmpty($events); - $this->assertInternalType('array', $events); - $this->assertArrayHasKey('task.move.column', $events); - } - - public function testCreateAction() - { - $action_id = $this->client->createAction(1, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1)); - $this->assertNotFalse($action_id); - $this->assertEquals(1, $action_id); - } - - public function testGetActions() - { - $actions = $this->client->getActions(1); - $this->assertNotEmpty($actions); - $this->assertInternalType('array', $actions); - $this->assertCount(1, $actions); - $this->assertArrayHasKey('id', $actions[0]); - $this->assertArrayHasKey('project_id', $actions[0]); - $this->assertArrayHasKey('event_name', $actions[0]); - $this->assertArrayHasKey('action_name', $actions[0]); - $this->assertArrayHasKey('params', $actions[0]); - $this->assertArrayHasKey('column_id', $actions[0]['params']); - } - - public function testRemoveAction() - { - $this->assertTrue($this->client->removeAction(1)); - - $actions = $this->client->getActions(1); - $this->assertEmpty($actions); - $this->assertCount(0, $actions); - } - - public function testGetAllLinks() - { - $links = $this->client->getAllLinks(); - $this->assertNotEmpty($links); - $this->assertArrayHasKey('id', $links[0]); - $this->assertArrayHasKey('label', $links[0]); - $this->assertArrayHasKey('opposite_id', $links[0]); - } - - public function testGetOppositeLink() - { - $link = $this->client->getOppositeLinkId(1); - $this->assertEquals(1, $link); - - $link = $this->client->getOppositeLinkId(2); - $this->assertEquals(3, $link); - } - - public function testGetLinkByLabel() - { - $link = $this->client->getLinkByLabel('blocks'); - $this->assertNotEmpty($link); - $this->assertEquals(2, $link['id']); - $this->assertEquals(3, $link['opposite_id']); - } - - public function testGetLinkById() - { - $link = $this->client->getLinkById(4); - $this->assertNotEmpty($link); - $this->assertEquals(4, $link['id']); - $this->assertEquals(5, $link['opposite_id']); - $this->assertEquals('duplicates', $link['label']); - } - - public function testCreateLink() - { - $link_id = $this->client->createLink(array('label' => 'test')); - $this->assertNotFalse($link_id); - $this->assertInternalType('int', $link_id); - - $link_id = $this->client->createLink(array('label' => 'foo', 'opposite_label' => 'bar')); - $this->assertNotFalse($link_id); - $this->assertInternalType('int', $link_id); - } - - public function testUpdateLink() - { - $link1 = $this->client->getLinkByLabel('bar'); - $this->assertNotEmpty($link1); - - $link2 = $this->client->getLinkByLabel('test'); - $this->assertNotEmpty($link2); - - $this->assertNotFalse($this->client->updateLink($link1['id'], $link2['id'], 'boo')); - - $link = $this->client->getLinkById($link1['id']); - $this->assertNotEmpty($link); - $this->assertEquals($link2['id'], $link['opposite_id']); - $this->assertEquals('boo', $link['label']); - - $this->assertTrue($this->client->removeLink($link1['id'])); - } - - public function testCreateTaskLink() - { - $task_id1 = $this->client->createTask(array('project_id' => 1, 'title' => 'A')); - $this->assertNotFalse($task_id1); - - $task_id2 = $this->client->createTask(array('project_id' => 1, 'title' => 'B')); - $this->assertNotFalse($task_id2); - - $task_id3 = $this->client->createTask(array('project_id' => 1, 'title' => 'C')); - $this->assertNotFalse($task_id3); - - $task_link_id = $this->client->createTaskLink($task_id1, $task_id2, 1); - $this->assertNotFalse($task_link_id); - - $task_link = $this->client->getTaskLinkById($task_link_id); - $this->assertNotEmpty($task_link); - $this->assertEquals($task_id1, $task_link['task_id']); - $this->assertEquals($task_id2, $task_link['opposite_task_id']); - $this->assertEquals(1, $task_link['link_id']); - - $task_links = $this->client->getAllTaskLinks($task_id1); - $this->assertNotEmpty($task_links); - $this->assertCount(1, $task_links); - - $this->assertTrue($this->client->updateTaskLink($task_link_id, $task_id1, $task_id3, 2)); - - $task_link = $this->client->getTaskLinkById($task_link_id); - $this->assertNotEmpty($task_link); - $this->assertEquals($task_id1, $task_link['task_id']); - $this->assertEquals($task_id3, $task_link['opposite_task_id']); - $this->assertEquals(2, $task_link['link_id']); - - $this->assertTrue($this->client->removeTaskLink($task_link_id)); - $this->assertEmpty($this->client->getAllTaskLinks($task_id1)); - } - - public function testCreateFile() - { - $this->assertNotFalse($this->client->createFile(1, $this->getTaskId(), 'My file', base64_encode('plain text file'))); - } - - public function testGetAllFiles() - { - $files = $this->client->getAllFiles(array('task_id' => $this->getTaskId())); - - $this->assertNotEmpty($files); - $this->assertCount(1, $files); - $this->assertEquals('My file', $files[0]['name']); - - $file = $this->client->getFile($files[0]['id']); - $this->assertNotEmpty($file); - $this->assertEquals('My file', $file['name']); - - $content = $this->client->downloadFile($file['id']); - $this->assertNotEmpty($content); - $this->assertEquals('plain text file', base64_decode($content)); - - $content = $this->client->downloadFile(1234567); - $this->assertEmpty($content); - - $this->assertTrue($this->client->removeFile($file['id'])); - $this->assertEmpty($this->client->getAllFiles(1)); - } - - public function testRemoveAllFiles() - { - $this->assertNotFalse($this->client->createFile(1, $this->getTaskId(), 'My file 1', base64_encode('plain text file'))); - $this->assertNotFalse($this->client->createFile(1, $this->getTaskId(), 'My file 2', base64_encode('plain text file'))); - - $files = $this->client->getAllFiles(array('task_id' => $this->getTaskId())); - $this->assertNotEmpty($files); - $this->assertCount(2, $files); - - $this->assertTrue($this->client->removeAllFiles(array('task_id' => $this->getTaskId()))); - - $files = $this->client->getAllFiles(array('task_id' => $this->getTaskId())); - $this->assertEmpty($files); - } - - public function testCreateTaskWithReference() - { - $task = array( - 'title' => 'Task with external ticket number', - 'reference' => 'TICKET-1234', - 'project_id' => 1, - 'description' => '[Link to my ticket](http://my-ticketing-system/1234)', - ); - - $task_id = $this->client->createTask($task); - - $this->assertNotFalse($task_id); - $this->assertInternalType('int', $task_id); - $this->assertTrue($task_id > 0); - } - - public function testGetTaskByReference() - { - $task = $this->client->getTaskByReference(array('project_id' => 1, 'reference' => 'TICKET-1234')); - - $this->assertNotEmpty($task); - $this->assertEquals('Task with external ticket number', $task['title']); - $this->assertEquals('TICKET-1234', $task['reference']); - $this->assertEquals('http://127.0.0.1:8000/?controller=TaskViewController&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'], $task['url']); - } - - public function testCreateOverdueTask() - { - $this->assertNotFalse($this->client->createTask(array( - 'title' => 'overdue task', - 'project_id' => 1, - 'date_due' => date('Y-m-d', strtotime('-2days')), - ))); - } - - public function testGetOverdueTasksByProject() - { - $tasks = $this->client->getOverdueTasksByProject(1); - $this->assertNotEmpty($tasks); - $this->assertCount(1, $tasks); - $this->assertEquals('overdue task', $tasks[0]['title']); - $this->assertEquals('API test', $tasks[0]['project_name']); - } - - public function testGetOverdueTasks() - { - $tasks = $this->client->getOverdueTasks(); - $this->assertNotEmpty($tasks); - $this->assertCount(1, $tasks); - $this->assertEquals('overdue task', $tasks[0]['title']); - $this->assertEquals('API test', $tasks[0]['project_name']); - } -} diff --git a/tests/integration/AppTest.php b/tests/integration/AppProcedureTest.php index 6575fbb8..06135dac 100644 --- a/tests/integration/AppTest.php +++ b/tests/integration/AppProcedureTest.php @@ -1,8 +1,8 @@ <?php -require_once __DIR__.'/Base.php'; +require_once __DIR__.'/BaseProcedureTest.php'; -class AppTest extends Base +class AppProcedureTest extends BaseProcedureTest { public function testGetTimezone() { @@ -31,4 +31,24 @@ class AppTest extends Base $this->assertEquals('Project Member', $roles['project-member']); $this->assertEquals('Project Viewer', $roles['project-viewer']); } + + public function testGetDefaultColor() + { + $this->assertEquals('yellow', $this->user->getDefaultTaskColor()); + } + + public function testGetDefaultColors() + { + $colors = $this->user->getDefaultTaskColors(); + $this->assertNotEmpty($colors); + $this->assertArrayHasKey('red', $colors); + } + + public function testGetColorList() + { + $colors = $this->user->getColorList(); + $this->assertNotEmpty($colors); + $this->assertArrayHasKey('red', $colors); + $this->assertEquals('Red', $colors['red']); + } } diff --git a/tests/integration/Base.php b/tests/integration/Base.php deleted file mode 100644 index 6f3ae076..00000000 --- a/tests/integration/Base.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -require_once __DIR__.'/../../vendor/autoload.php'; - -abstract class Base extends PHPUnit_Framework_TestCase -{ - protected $app = null; - protected $admin = null; - protected $user = null; - - public static function setUpBeforeClass() - { - if (DB_DRIVER === 'sqlite') { - @unlink(DB_FILENAME); - } elseif (DB_DRIVER === 'mysql') { - $pdo = new PDO('mysql:host='.DB_HOSTNAME, DB_USERNAME, DB_PASSWORD); - $pdo->exec('DROP DATABASE '.DB_NAME); - $pdo->exec('CREATE DATABASE '.DB_NAME); - $pdo = null; - } elseif (DB_DRIVER === 'postgres') { - $pdo = new PDO('pgsql:host='.DB_HOSTNAME, DB_USERNAME, DB_PASSWORD); - $pdo->exec('DROP DATABASE '.DB_NAME); - $pdo->exec('CREATE DATABASE '.DB_NAME.' WITH OWNER '.DB_USERNAME); - $pdo = null; - } - - $service = new Kanboard\ServiceProvider\DatabaseProvider; - - $db = $service->getInstance(); - $db->table('settings')->eq('option', 'api_token')->update(array('value' => API_KEY)); - $db->closeConnection(); - } - - public function setUp() - { - $this->app = new JsonRPC\Client(API_URL); - $this->app->authentication('jsonrpc', API_KEY); - $this->app->getHttpClient()->withDebug(); - - $this->admin = new JsonRPC\Client(API_URL); - $this->admin->authentication('admin', 'admin'); - $this->admin->getHttpClient()->withDebug(); - - $this->user = new JsonRPC\Client(API_URL); - $this->user->authentication('user', 'password'); - $this->user->getHttpClient()->withDebug(); - } - - protected function getProjectId() - { - $projects = $this->app->getAllProjects(); - $this->assertNotEmpty($projects); - return $projects[0]['id']; - } - - protected function getGroupId() - { - $groups = $this->app->getAllGroups(); - $this->assertNotEmpty($groups); - return $groups[0]['id']; - } -} diff --git a/tests/integration/BaseProcedureTest.php b/tests/integration/BaseProcedureTest.php new file mode 100644 index 00000000..e3382e82 --- /dev/null +++ b/tests/integration/BaseProcedureTest.php @@ -0,0 +1,122 @@ +<?php + +require_once __DIR__.'/../../vendor/autoload.php'; + +abstract class BaseProcedureTest extends PHPUnit_Framework_TestCase +{ + protected $app = null; + protected $admin = null; + protected $manager = null; + protected $user = null; + + protected $adminUserId = 0; + protected $managerUserId = 0; + protected $userUserId = 0; + + protected $projectName = ''; + protected $projectId = 0; + protected $taskTitle = 'My task'; + protected $taskId = 0; + + protected $groupName1 = 'My Group A'; + protected $groupName2 = 'My Group B'; + protected $groupId1; + protected $groupId2; + + protected $username = 'test-user'; + protected $userId; + + public function setUp() + { + $this->setUpAppClient(); + $this->setUpAdminUser(); + $this->setUpManagerUser(); + $this->setUpStandardUser(); + } + + public function setUpAppClient() + { + $this->app = new JsonRPC\Client(API_URL); + $this->app->authentication('jsonrpc', API_KEY); + $this->app->getHttpClient()->withDebug()->withTimeout(10); + } + + public function setUpAdminUser() + { + $this->adminUserId = $this->getUserId('superuser'); + + if (! $this->adminUserId) { + $this->adminUserId = $this->app->createUser('superuser', 'password', 'Admin User', 'user@localhost', 'app-admin'); + $this->assertNotFalse($this->adminUserId); + } + + $this->admin = new JsonRPC\Client(API_URL); + $this->admin->authentication('superuser', 'password'); + $this->admin->getHttpClient()->withDebug(); + } + + public function setUpManagerUser() + { + $this->managerUserId = $this->getUserId('manager'); + + if (! $this->managerUserId) { + $this->managerUserId = $this->app->createUser('manager', 'password', 'Manager User', 'user@localhost', 'app-manager'); + $this->assertNotFalse($this->managerUserId); + } + + $this->manager = new JsonRPC\Client(API_URL); + $this->manager->authentication('manager', 'password'); + $this->manager->getHttpClient()->withDebug(); + } + + public function setUpStandardUser() + { + $this->userUserId = $this->getUserId('user'); + + if (! $this->userUserId) { + $this->userUserId = $this->app->createUser('user', 'password', 'Standard User', 'user@localhost', 'app-user'); + $this->assertNotFalse($this->userUserId); + } + + $this->user = new JsonRPC\Client(API_URL); + $this->user->authentication('user', 'password'); + $this->user->getHttpClient()->withDebug(); + } + + public function getUserId($username) + { + $user = $this->app->getUserByName($username); + + if (! empty($user)) { + return $user['id']; + } + + return 0; + } + + public function assertCreateTeamProject() + { + $this->projectId = $this->app->createProject($this->projectName, 'Description'); + $this->assertNotFalse($this->projectId); + } + + public function assertCreateUser() + { + $this->userId = $this->app->createUser($this->username, 'password'); + $this->assertNotFalse($this->userId); + } + + public function assertCreateGroups() + { + $this->groupId1 = $this->app->createGroup($this->groupName1); + $this->groupId2 = $this->app->createGroup($this->groupName2, 'External ID'); + $this->assertNotFalse($this->groupId1); + $this->assertNotFalse($this->groupId2); + } + + public function assertCreateTask() + { + $this->taskId = $this->app->createTask(array('title' => $this->taskTitle, 'project_id' => $this->projectId)); + $this->assertNotFalse($this->taskId); + } +} diff --git a/tests/integration/BoardProcedureTest.php b/tests/integration/BoardProcedureTest.php new file mode 100644 index 00000000..273e93c7 --- /dev/null +++ b/tests/integration/BoardProcedureTest.php @@ -0,0 +1,25 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class BoardProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test board'; + + public function testAll() + { + $this->assertCreateTeamProject(); + $this->assertGetBoard(); + } + + public function assertGetBoard() + { + $board = $this->app->getBoard($this->projectId); + $this->assertNotNull($board); + $this->assertCount(1, $board); + $this->assertEquals('Default swimlane', $board[0]['name']); + + $this->assertCount(4, $board[0]['columns']); + $this->assertEquals('Ready', $board[0]['columns'][1]['title']); + } +} diff --git a/tests/integration/BoardTest.php b/tests/integration/BoardTest.php deleted file mode 100644 index bf8d50b9..00000000 --- a/tests/integration/BoardTest.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -require_once __DIR__.'/Base.php'; - -class BoardTest extends Base -{ - public function testCreateProject() - { - $this->assertEquals(1, $this->app->createProject('A project')); - } - - public function testGetBoard() - { - $board = $this->app->getBoard(1); - $this->assertCount(1, $board); - $this->assertEquals('Default swimlane', $board[0]['name']); - - $this->assertCount(4, $board[0]['columns']); - $this->assertEquals('Ready', $board[0]['columns'][1]['title']); - } -} diff --git a/tests/integration/CategoryProcedureTest.php b/tests/integration/CategoryProcedureTest.php new file mode 100644 index 00000000..2f5294ba --- /dev/null +++ b/tests/integration/CategoryProcedureTest.php @@ -0,0 +1,76 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class CategoryProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test categories'; + private $categoryId = 0; + + public function testAll() + { + $this->assertCreateTeamProject(); + $this->assertCreateCategory(); + $this->assertThatCategoriesAreUnique(); + $this->assertGetCategory(); + $this->assertGetAllCategories(); + $this->assertCategoryUpdate(); + $this->assertRemoveCategory(); + } + + public function assertCreateCategory() + { + $this->categoryId = $this->app->createCategory(array( + 'name' => 'Category', + 'project_id' => $this->projectId, + )); + + $this->assertNotFalse($this->categoryId); + } + + public function assertThatCategoriesAreUnique() + { + $this->assertFalse($this->app->execute('createCategory', array( + 'name' => 'Category', + 'project_id' => $this->projectId, + ))); + } + + public function assertGetCategory() + { + $category = $this->app->getCategory($this->categoryId); + + $this->assertInternalType('array', $category); + $this->assertEquals($this->categoryId, $category['id']); + $this->assertEquals('Category', $category['name']); + $this->assertEquals($this->projectId, $category['project_id']); + } + + public function assertGetAllCategories() + { + $categories = $this->app->getAllCategories($this->projectId); + + $this->assertCount(1, $categories); + $this->assertEquals($this->categoryId, $categories[0]['id']); + $this->assertEquals('Category', $categories[0]['name']); + $this->assertEquals($this->projectId, $categories[0]['project_id']); + } + + public function assertCategoryUpdate() + { + $this->assertTrue($this->app->execute('updateCategory', array( + 'id' => $this->categoryId, + 'name' => 'Renamed category', + ))); + + $category = $this->app->getCategory($this->categoryId); + $this->assertEquals('Renamed category', $category['name']); + } + + public function assertRemoveCategory() + { + $this->assertTrue($this->app->removeCategory($this->categoryId)); + $this->assertFalse($this->app->removeCategory($this->categoryId)); + $this->assertFalse($this->app->removeCategory(1111)); + } +} diff --git a/tests/integration/ColumnProcedureTest.php b/tests/integration/ColumnProcedureTest.php new file mode 100644 index 00000000..fb6a27c3 --- /dev/null +++ b/tests/integration/ColumnProcedureTest.php @@ -0,0 +1,69 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class ColumnProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test columns'; + private $columns = array(); + + public function testAll() + { + $this->assertCreateTeamProject(); + $this->assertGetColumns(); + $this->assertUpdateColumn(); + $this->assertAddColumn(); + $this->assertRemoveColumn(); + $this->assertChangeColumnPosition(); + } + + public function assertGetColumns() + { + $this->columns = $this->app->getColumns($this->projectId); + $this->assertCount(4, $this->columns); + $this->assertEquals('Done', $this->columns[3]['title']); + } + + public function assertUpdateColumn() + { + $this->assertTrue($this->app->updateColumn($this->columns[3]['id'], 'Another column', 2)); + + $this->columns = $this->app->getColumns($this->projectId); + $this->assertEquals('Another column', $this->columns[3]['title']); + $this->assertEquals(2, $this->columns[3]['task_limit']); + } + + public function assertAddColumn() + { + $column_id = $this->app->addColumn($this->projectId, 'New column'); + $this->assertNotFalse($column_id); + $this->assertTrue($column_id > 0); + + $this->columns = $this->app->getColumns($this->projectId); + $this->assertCount(5, $this->columns); + $this->assertEquals('New column', $this->columns[4]['title']); + } + + public function assertRemoveColumn() + { + $this->assertTrue($this->app->removeColumn($this->columns[3]['id'])); + + $this->columns = $this->app->getColumns($this->projectId); + $this->assertCount(4, $this->columns); + } + + public function assertChangeColumnPosition() + { + $this->assertTrue($this->app->changeColumnPosition($this->projectId, $this->columns[0]['id'], 3)); + + $this->columns = $this->app->getColumns($this->projectId); + $this->assertEquals('Ready', $this->columns[0]['title']); + $this->assertEquals(1, $this->columns[0]['position']); + $this->assertEquals('Work in progress', $this->columns[1]['title']); + $this->assertEquals(2, $this->columns[1]['position']); + $this->assertEquals('Backlog', $this->columns[2]['title']); + $this->assertEquals(3, $this->columns[2]['position']); + $this->assertEquals('New column', $this->columns[3]['title']); + $this->assertEquals(4, $this->columns[3]['position']); + } +} diff --git a/tests/integration/ColumnTest.php b/tests/integration/ColumnTest.php deleted file mode 100644 index 6d02afc0..00000000 --- a/tests/integration/ColumnTest.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -require_once __DIR__.'/Base.php'; - -class ColumnTest extends Base -{ - public function testCreateProject() - { - $this->assertEquals(1, $this->app->createProject('A project')); - } - - public function testGetColumns() - { - $columns = $this->app->getColumns($this->getProjectId()); - $this->assertCount(4, $columns); - $this->assertEquals('Done', $columns[3]['title']); - } - - public function testUpdateColumn() - { - $this->assertTrue($this->app->updateColumn(4, 'Boo', 2)); - - $columns = $this->app->getColumns($this->getProjectId()); - $this->assertEquals('Boo', $columns[3]['title']); - $this->assertEquals(2, $columns[3]['task_limit']); - } - - public function testAddColumn() - { - $column_id = $this->app->addColumn($this->getProjectId(), 'New column'); - - $this->assertNotFalse($column_id); - $this->assertInternalType('int', $column_id); - $this->assertTrue($column_id > 0); - - $columns = $this->app->getColumns($this->getProjectId()); - $this->assertCount(5, $columns); - $this->assertEquals('New column', $columns[4]['title']); - } - - public function testRemoveColumn() - { - $this->assertTrue($this->app->removeColumn(5)); - - $columns = $this->app->getColumns($this->getProjectId()); - $this->assertCount(4, $columns); - } - - public function testChangeColumnPosition() - { - $this->assertTrue($this->app->changeColumnPosition($this->getProjectId(), 1, 3)); - - $columns = $this->app->getColumns($this->getProjectId()); - $this->assertCount(4, $columns); - - $this->assertEquals('Ready', $columns[0]['title']); - $this->assertEquals(1, $columns[0]['position']); - $this->assertEquals('Work in progress', $columns[1]['title']); - $this->assertEquals(2, $columns[1]['position']); - $this->assertEquals('Backlog', $columns[2]['title']); - $this->assertEquals(3, $columns[2]['position']); - $this->assertEquals('Boo', $columns[3]['title']); - $this->assertEquals(4, $columns[3]['position']); - } -} diff --git a/tests/integration/CommentProcedureTest.php b/tests/integration/CommentProcedureTest.php new file mode 100644 index 00000000..881d938c --- /dev/null +++ b/tests/integration/CommentProcedureTest.php @@ -0,0 +1,63 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class CommentProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test comments'; + private $commentId = 0; + + public function testAll() + { + $this->assertCreateTeamProject(); + $this->assertCreateTask(); + $this->assertCreateComment(); + $this->assertUpdateComment(); + $this->assertGetAllComments(); + $this->assertRemoveComment(); + } + + public function assertCreateComment() + { + $this->commentId = $this->app->execute('createComment', array( + 'task_id' => $this->taskId, + 'user_id' => 1, + 'content' => 'foobar', + )); + + $this->assertNotFalse($this->commentId); + } + + public function assertGetComment() + { + $comment = $this->app->getComment($this->commentId); + $this->assertNotFalse($comment); + $this->assertNotEmpty($comment); + $this->assertEquals(1, $comment['user_id']); + $this->assertEquals('foobar', $comment['comment']); + } + + public function assertUpdateComment() + { + $this->assertTrue($this->app->execute('updateComment', array( + 'id' => $this->commentId, + 'content' => 'test', + ))); + + $comment = $this->app->getComment($this->commentId); + $this->assertEquals('test', $comment['comment']); + } + + public function assertGetAllComments() + { + $comments = $this->app->getAllComments($this->taskId); + $this->assertCount(1, $comments); + $this->assertEquals('test', $comments[0]['comment']); + } + + public function assertRemoveComment() + { + $this->assertTrue($this->app->removeComment($this->commentId)); + $this->assertFalse($this->app->removeComment($this->commentId)); + } +} diff --git a/tests/integration/GroupMemberProcedureTest.php b/tests/integration/GroupMemberProcedureTest.php new file mode 100644 index 00000000..fe243533 --- /dev/null +++ b/tests/integration/GroupMemberProcedureTest.php @@ -0,0 +1,53 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class GroupMemberProcedureTest extends BaseProcedureTest +{ + protected $username = 'user-group-member'; + protected $groupName1 = 'My group member A'; + protected $groupName2 = 'My group member B'; + + public function testAll() + { + $this->assertCreateGroups(); + $this->assertCreateUser(); + $this->assertAddMember(); + $this->assertGetMembers(); + $this->assertIsGroupMember(); + $this->assertGetGroups(); + $this->assertRemove(); + } + + public function assertAddMember() + { + $this->assertTrue($this->app->addGroupMember($this->groupId1, $this->userId)); + } + + public function assertGetMembers() + { + $members = $this->app->getGroupMembers($this->groupId1); + $this->assertCount(1, $members); + $this->assertEquals($this->username, $members[0]['username']); + } + + public function assertIsGroupMember() + { + $this->assertTrue($this->app->isGroupMember($this->groupId1, $this->userId)); + $this->assertFalse($this->app->isGroupMember($this->groupId1, $this->adminUserId)); + } + + public function assertGetGroups() + { + $groups = $this->app->getMemberGroups($this->userId); + $this->assertCount(1, $groups); + $this->assertEquals($this->groupId1, $groups[0]['id']); + $this->assertEquals($this->groupName1, $groups[0]['name']); + } + + public function assertRemove() + { + $this->assertTrue($this->app->removeGroupMember($this->groupId1, $this->userId)); + $this->assertFalse($this->app->isGroupMember($this->groupId1, $this->userId)); + } +} diff --git a/tests/integration/GroupMemberTest.php b/tests/integration/GroupMemberTest.php deleted file mode 100644 index d49945b5..00000000 --- a/tests/integration/GroupMemberTest.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -require_once __DIR__.'/Base.php'; - -class GroupMemberTest extends Base -{ - public function testAddMember() - { - $this->assertNotFalse($this->app->createGroup('My Group A')); - $this->assertNotFalse($this->app->createGroup('My Group B')); - - $groupId = $this->getGroupId(); - $this->assertTrue($this->app->addGroupMember($groupId, 1)); - } - - public function testGetMembers() - { - $groups = $this->app->getAllGroups(); - $members = $this->app->getGroupMembers($groups[0]['id']); - $this->assertCount(1, $members); - $this->assertEquals('admin', $members[0]['username']); - - $this->assertSame(array(), $this->app->getGroupMembers($groups[1]['id'])); - } - - public function testIsGroupMember() - { - $groupId = $this->getGroupId(); - $this->assertTrue($this->app->isGroupMember($groupId, 1)); - $this->assertFalse($this->app->isGroupMember($groupId, 2)); - } - - public function testGetGroups() - { - $groups = $this->app->getMemberGroups(1); - $this->assertCount(1, $groups); - $this->assertEquals(1, $groups[0]['id']); - $this->assertEquals('My Group A', $groups[0]['name']); - } - - public function testRemove() - { - $groupId = $this->getGroupId(); - $this->assertTrue($this->app->removeGroupMember($groupId, 1)); - $this->assertFalse($this->app->isGroupMember($groupId, 1)); - } -} diff --git a/tests/integration/GroupProcedureTest.php b/tests/integration/GroupProcedureTest.php new file mode 100644 index 00000000..610c121d --- /dev/null +++ b/tests/integration/GroupProcedureTest.php @@ -0,0 +1,50 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class GroupProcedureTest extends BaseProcedureTest +{ + public function testAll() + { + $this->assertCreateGroups(); + $this->assertGetAllGroups(); + $this->assertGetGroup(); + $this->assertUpdateGroup(); + $this->assertRemove(); + } + + public function assertGetAllGroups() + { + $groups = $this->app->getAllGroups(); + $this->assertNotEmpty($groups); + $this->assertArrayHasKey('name', $groups[0]); + $this->assertArrayHasKey('external_id', $groups[0]); + } + + public function assertGetGroup() + { + $group = $this->app->getGroup($this->groupId1); + $this->assertNotEmpty($group); + $this->assertEquals($this->groupName1, $group['name']); + $this->assertEquals('', $group['external_id']); + } + + public function assertUpdateGroup() + { + $this->assertTrue($this->app->updateGroup(array( + 'group_id' => $this->groupId2, + 'name' => 'My Group C', + 'external_id' => 'something else', + ))); + + $group = $this->app->getGroup($this->groupId2); + $this->assertNotEmpty($group); + $this->assertEquals('My Group C', $group['name']); + $this->assertEquals('something else', $group['external_id']); + } + + public function assertRemove() + { + $this->assertTrue($this->app->removeGroup($this->groupId1)); + } +} diff --git a/tests/integration/GroupTest.php b/tests/integration/GroupTest.php deleted file mode 100644 index 7a5bccc9..00000000 --- a/tests/integration/GroupTest.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -require_once __DIR__.'/Base.php'; - -class GroupTest extends Base -{ - public function testCreateGroup() - { - $this->assertNotFalse($this->app->createGroup('My Group A')); - $this->assertNotFalse($this->app->createGroup('My Group B', '1234')); - } - - public function testGetter() - { - $groups = $this->app->getAllGroups(); - $this->assertCount(2, $groups); - $this->assertEquals('My Group A', $groups[0]['name']); - $this->assertEquals('', $groups[0]['external_id']); - $this->assertEquals('My Group B', $groups[1]['name']); - $this->assertEquals('1234', $groups[1]['external_id']); - - $group = $this->app->getGroup($groups[0]['id']); - $this->assertNotEmpty($group); - $this->assertEquals('My Group A', $group['name']); - $this->assertEquals('', $group['external_id']); - } - - public function testUpdate() - { - $groups = $this->app->getAllGroups(); - - $this->assertTrue($this->app->updateGroup(array('group_id' => $groups[0]['id'], 'name' => 'ABC', 'external_id' => 'something'))); - $this->assertTrue($this->app->updateGroup(array('group_id' => $groups[1]['id'], 'external_id' => ''))); - - $groups = $this->app->getAllGroups(); - $this->assertEquals('ABC', $groups[0]['name']); - $this->assertEquals('something', $groups[0]['external_id']); - $this->assertEquals('', $groups[1]['external_id']); - } - - public function testRemove() - { - $groups = $this->app->getAllGroups(); - $this->assertTrue($this->app->removeGroup($groups[0]['id'])); - $this->assertTrue($this->app->removeGroup($groups[1]['id'])); - $this->assertSame(array(), $this->app->getAllGroups()); - } -} diff --git a/tests/integration/LinkProcedureTest.php b/tests/integration/LinkProcedureTest.php new file mode 100644 index 00000000..fb07e694 --- /dev/null +++ b/tests/integration/LinkProcedureTest.php @@ -0,0 +1,70 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class LinkProcedureTest extends BaseProcedureTest +{ + public function testGetAllLinks() + { + $links = $this->app->getAllLinks(); + $this->assertNotEmpty($links); + $this->assertArrayHasKey('id', $links[0]); + $this->assertArrayHasKey('label', $links[0]); + $this->assertArrayHasKey('opposite_id', $links[0]); + } + + public function testGetOppositeLink() + { + $link = $this->app->getOppositeLinkId(1); + $this->assertEquals(1, $link); + + $link = $this->app->getOppositeLinkId(2); + $this->assertEquals(3, $link); + } + + public function testGetLinkByLabel() + { + $link = $this->app->getLinkByLabel('blocks'); + $this->assertNotEmpty($link); + $this->assertEquals(2, $link['id']); + $this->assertEquals(3, $link['opposite_id']); + } + + public function testGetLinkById() + { + $link = $this->app->getLinkById(4); + $this->assertNotEmpty($link); + $this->assertEquals(4, $link['id']); + $this->assertEquals(5, $link['opposite_id']); + $this->assertEquals('duplicates', $link['label']); + } + + public function testCreateLink() + { + $link_id = $this->app->createLink(array('label' => 'test')); + $this->assertNotFalse($link_id); + $this->assertInternalType('int', $link_id); + + $link_id = $this->app->createLink(array('label' => 'foo', 'opposite_label' => 'bar')); + $this->assertNotFalse($link_id); + $this->assertInternalType('int', $link_id); + } + + public function testUpdateLink() + { + $link1 = $this->app->getLinkByLabel('bar'); + $this->assertNotEmpty($link1); + + $link2 = $this->app->getLinkByLabel('test'); + $this->assertNotEmpty($link2); + + $this->assertNotFalse($this->app->updateLink($link1['id'], $link2['id'], 'my link')); + + $link = $this->app->getLinkById($link1['id']); + $this->assertNotEmpty($link); + $this->assertEquals($link2['id'], $link['opposite_id']); + $this->assertEquals('my link', $link['label']); + + $this->assertTrue($this->app->removeLink($link1['id'])); + } +} diff --git a/tests/integration/MeProcedureTest.php b/tests/integration/MeProcedureTest.php new file mode 100644 index 00000000..2106419c --- /dev/null +++ b/tests/integration/MeProcedureTest.php @@ -0,0 +1,68 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class MeProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My private project'; + + public function testAll() + { + $this->assertGetMe(); + $this->assertCreateMyPrivateProject(); + $this->assertGetMyProjectsList(); + $this->assertGetMyProjects(); + $this->assertCreateTask(); + $this->assertGetMyDashboard(); + $this->assertGetMyActivityStream(); + } + + public function assertGetMe() + { + $profile = $this->user->getMe(); + $this->assertEquals('user', $profile['username']); + $this->assertEquals('app-user', $profile['role']); + } + + public function assertCreateMyPrivateProject() + { + $this->projectId = $this->user->createMyPrivateProject($this->projectName); + $this->assertNotFalse($this->projectId); + } + + public function assertGetMyProjectsList() + { + $projects = $this->user->getMyProjectsList(); + $this->assertNotEmpty($projects); + $this->assertEquals($this->projectName, $projects[$this->projectId]); + } + + public function assertGetMyProjects() + { + $projects = $this->user->getMyProjects(); + $this->assertNotEmpty($projects); + } + + public function assertCreateTask() + { + $taskId = $this->user->createTask(array('title' => 'My task', 'project_id' => $this->projectId, 'owner_id' => $this->userUserId)); + $this->assertNotFalse($taskId); + } + + public function assertGetMyDashboard() + { + $dashboard = $this->user->getMyDashboard(); + $this->assertNotEmpty($dashboard); + $this->assertArrayHasKey('projects', $dashboard); + $this->assertArrayHasKey('tasks', $dashboard); + $this->assertArrayHasKey('subtasks', $dashboard); + $this->assertNotEmpty($dashboard['projects']); + $this->assertNotEmpty($dashboard['tasks']); + } + + public function assertGetMyActivityStream() + { + $activity = $this->user->getMyActivityStream(); + $this->assertNotEmpty($activity); + } +} diff --git a/tests/integration/MeTest.php b/tests/integration/MeTest.php deleted file mode 100644 index 1b028b84..00000000 --- a/tests/integration/MeTest.php +++ /dev/null @@ -1,247 +0,0 @@ -<?php - -require_once __DIR__.'/Base.php'; - -class MeTest extends Base -{ - public function testCreateProject() - { - $this->assertEquals(1, $this->app->createProject('team project')); - } - - public function testCreateUser() - { - $this->assertEquals(2, $this->app->createUser('user', 'password')); - } - - /** - * @expectedException JsonRPC\Exception\AccessDeniedException - */ - public function testNotAllowedAppProcedure() - { - $this->app->getMe(); - } - - /** - * @expectedException JsonRPC\Exception\AccessDeniedException - */ - public function testNotAllowedUserProcedure() - { - $this->user->getAllProjects(); - } - - /** - * @expectedException JsonRPC\Exception\AccessDeniedException - */ - public function testNotAllowedProjectForUser() - { - $this->user->getProjectById(1); - } - - public function testAllowedProjectForAdmin() - { - $this->assertNotEmpty($this->admin->getProjectById(1)); - } - - public function testGetTimezone() - { - $this->assertEquals('UTC', $this->user->getTimezone()); - } - - public function testGetVersion() - { - $this->assertEquals('master', $this->user->getVersion()); - } - - public function testGetDefaultColor() - { - $this->assertEquals('yellow', $this->user->getDefaultTaskColor()); - } - - public function testGetDefaultColors() - { - $colors = $this->user->getDefaultTaskColors(); - $this->assertNotEmpty($colors); - $this->assertArrayHasKey('red', $colors); - } - - public function testGetColorList() - { - $colors = $this->user->getColorList(); - $this->assertNotEmpty($colors); - $this->assertArrayHasKey('red', $colors); - $this->assertEquals('Red', $colors['red']); - } - - public function testGetMe() - { - $profile = $this->user->getMe(); - $this->assertNotEmpty($profile); - $this->assertEquals(2, $profile['id']); - $this->assertEquals('user', $profile['username']); - } - - public function testCreateMyPrivateProject() - { - $this->assertEquals(2, $this->user->createMyPrivateProject('my project')); - } - - public function testGetMyProjectsList() - { - $projects = $this->user->getMyProjectsList(); - $this->assertNotEmpty($projects); - $this->assertArrayNotHasKey(1, $projects); - $this->assertArrayHasKey(2, $projects); - $this->assertEquals('my project', $projects[2]); - } - - public function testGetMyProjects() - { - $projects = $this->user->getMyProjects(); - $this->assertNotEmpty($projects); - $this->assertCount(1, $projects); - $this->assertEquals(2, $projects[0]['id']); - $this->assertEquals('my project', $projects[0]['name']); - $this->assertNotEmpty($projects[0]['url']['calendar']); - $this->assertNotEmpty($projects[0]['url']['board']); - $this->assertNotEmpty($projects[0]['url']['list']); - } - - public function testGetProjectById() - { - $project = $this->user->getProjectById(2); - $this->assertNotEmpty($project); - $this->assertEquals('my project', $project['name']); - $this->assertEquals(1, $project['is_private']); - } - - public function testCreateTask() - { - $this->assertEquals(1, $this->user->createTask('my user title', 2)); - $this->assertEquals(2, $this->admin->createTask('my admin title', 1)); - } - - public function testCreateTaskWithWrongMember() - { - $this->assertFalse($this->user->createTask(array('title' => 'something', 'project_id' => 2, 'owner_id' => 1))); - $this->assertFalse($this->app->createTask(array('title' => 'something', 'project_id' => 1, 'owner_id' => 2))); - } - - public function testGetTask() - { - $task = $this->user->getTask(1); - $this->assertNotEmpty($task); - $this->assertEquals('my user title', $task['title']); - $this->assertEquals('yellow', $task['color_id']); - $this->assertArrayHasKey('color', $task); - $this->assertArrayHasKey('name', $task['color']); - $this->assertArrayHasKey('border', $task['color']); - $this->assertArrayHasKey('background', $task['color']); - } - - /** - * @expectedException JsonRPC\Exception\AccessDeniedException - */ - public function testGetAdminTask() - { - $this->user->getTask(2); - } - - /** - * @expectedException JsonRPC\Exception\AccessDeniedException - */ - public function testGetProjectActivityDenied() - { - $this->user->getProjectActivity(1); - } - - public function testGetProjectActivityAllowed() - { - $activity = $this->user->getProjectActivity(2); - $this->assertNotEmpty($activity); - } - - public function testGetMyActivityStream() - { - $activity = $this->user->getMyActivityStream(); - $this->assertNotEmpty($activity); - } - - public function testCloseTask() - { - $this->assertTrue($this->user->closeTask(1)); - } - - public function testOpenTask() - { - $this->assertTrue($this->user->openTask(1)); - } - - public function testMoveTaskPosition() - { - $this->assertTrue($this->user->moveTaskPosition(2, 1, 2, 1)); - } - - public function testUpdateTaskWithWrongMember() - { - $this->assertFalse($this->user->updateTask(array('id' => 1, 'title' => 'new title', 'reference' => 'test', 'owner_id' => 1))); - } - - public function testUpdateTask() - { - $this->assertTrue($this->user->updateTask(array('id' => 1, 'title' => 'new title', 'reference' => 'test', 'owner_id' => 2))); - } - - public function testGetbyReference() - { - $task = $this->user->getTaskByReference(2, 'test'); - $this->assertNotEmpty($task); - $this->assertEquals('new title', $task['title']); - $this->assertEquals(2, $task['column_id']); - $this->assertEquals(1, $task['position']); - } - - public function testGetMyDashboard() - { - $dashboard = $this->user->getMyDashboard(); - $this->assertNotEmpty($dashboard); - $this->assertArrayHasKey('projects', $dashboard); - $this->assertArrayHasKey('tasks', $dashboard); - $this->assertArrayHasKey('subtasks', $dashboard); - $this->assertNotEmpty($dashboard['projects']); - $this->assertNotEmpty($dashboard['tasks']); - } - - public function testGetBoard() - { - $this->assertNotEmpty($this->user->getBoard(2)); - } - - public function testCreateOverdueTask() - { - $this->assertNotFalse($this->user->createTask(array( - 'title' => 'overdue task', - 'project_id' => 2, - 'date_due' => date('Y-m-d', strtotime('-2days')), - 'owner_id' => 2, - ))); - } - - public function testGetMyOverdueTasks() - { - $tasks = $this->user->getMyOverdueTasks(); - $this->assertNotEmpty($tasks); - $this->assertCount(1, $tasks); - $this->assertEquals('overdue task', $tasks[0]['title']); - $this->assertEquals('my project', $tasks[0]['project_name']); - } - - public function testGetOverdueTasksByProject() - { - $tasks = $this->user->getOverdueTasksByProject(2); - $this->assertNotEmpty($tasks); - $this->assertCount(1, $tasks); - $this->assertEquals('overdue task', $tasks[0]['title']); - $this->assertEquals('my project', $tasks[0]['project_name']); - } -} diff --git a/tests/integration/OverdueTaskProcedureTest.php b/tests/integration/OverdueTaskProcedureTest.php new file mode 100644 index 00000000..65f52301 --- /dev/null +++ b/tests/integration/OverdueTaskProcedureTest.php @@ -0,0 +1,43 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class OverdueTaskProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test overdue tasks'; + + public function testAll() + { + $this->assertCreateTeamProject(); + $this->assertCreateOverdueTask(); + $this->assertGetOverdueTasksByProject(); + $this->assertGetOverdueTasks(); + } + + public function assertCreateOverdueTask() + { + $this->assertNotFalse($this->app->createTask(array( + 'title' => 'overdue task', + 'project_id' => $this->projectId, + 'date_due' => date('Y-m-d', strtotime('-2days')), + ))); + } + + public function assertGetOverdueTasksByProject() + { + $tasks = $this->app->getOverdueTasksByProject($this->projectId); + $this->assertNotEmpty($tasks); + $this->assertCount(1, $tasks); + $this->assertEquals('overdue task', $tasks[0]['title']); + $this->assertEquals($this->projectName, $tasks[0]['project_name']); + } + + public function assertGetOverdueTasks() + { + $tasks = $this->app->getOverdueTasks(); + $this->assertNotEmpty($tasks); + $this->assertCount(1, $tasks); + $this->assertEquals('overdue task', $tasks[0]['title']); + $this->assertEquals($this->projectName, $tasks[0]['project_name']); + } +} diff --git a/tests/integration/ProcedureAuthorizationTest.php b/tests/integration/ProcedureAuthorizationTest.php new file mode 100644 index 00000000..a63e9d8c --- /dev/null +++ b/tests/integration/ProcedureAuthorizationTest.php @@ -0,0 +1,306 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class ProcedureAuthorizationTest extends BaseProcedureTest +{ + public function testApiCredentialDoNotHaveAccessToUserCredentialProcedure() + { + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->app->getMe(); + } + + public function testUserCredentialDoNotHaveAccessToAdminProcedures() + { + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->getUser(1); + } + + public function testManagerCredentialDoNotHaveAccessToAdminProcedures() + { + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->getAllProjects(); + } + + public function testUserCredentialDoNotHaveAccessToManagerProcedures() + { + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->createProject('Team project creation are only for app managers'); + } + + public function testAppManagerCanCreateTeamProject() + { + $this->assertNotFalse($this->manager->createProject('Team project created by app manager')); + } + + public function testAdminManagerCanCreateTeamProject() + { + $projectId = $this->admin->createProject('Team project created by admin'); + $this->assertNotFalse($projectId); + + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->assertNotNull($this->manager->getProjectById($projectId)); + } + + public function testProjectManagerCanUpdateHisProject() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Team project can be updated', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + $this->assertEquals('project-manager', $this->app->getProjectUserRole($projectId, $this->managerUserId)); + $this->assertNotNull($this->manager->getProjectById($projectId)); + + $this->assertTrue($this->manager->updateProject($projectId, 'My team project have been updated')); + } + + public function testProjectAuthorizationForbidden() + { + $projectId = $this->manager->createProject('A team project without members'); + $this->assertNotFalse($projectId); + + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->getProjectById($projectId); + } + + public function testProjectAuthorizationGranted() + { + $projectId = $this->manager->createProject(array( + 'name' => 'A team project with members', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId)); + $this->assertNotNull($this->user->getProjectById($projectId)); + } + + public function testActionAuthorizationForbidden() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + + $actionId = $this->manager->createAction($projectId, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1)); + $this->assertNotFalse($actionId); + + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->removeAction($projectId); + } + + public function testActionAuthorizationForbiddenBecauseNotProjectManager() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + + $actionId = $this->manager->createAction($projectId, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1)); + $this->assertNotFalse($actionId); + + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-member')); + + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->removeAction($actionId); + } + + public function testActionAuthorizationGranted() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + + $actionId = $this->manager->createAction($projectId, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1)); + $this->assertNotFalse($actionId); + + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-manager')); + $this->assertTrue($this->user->removeAction($actionId)); + } + + public function testCategoryAuthorizationForbidden() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + + $categoryId = $this->manager->createCategory($projectId, 'Test'); + $this->assertNotFalse($categoryId); + + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->removeCategory($categoryId); + } + + public function testCategoryAuthorizationForbiddenBecauseNotProjectManager() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + + $categoryId = $this->manager->createCategory($projectId, 'Test'); + $this->assertNotFalse($categoryId); + + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-member')); + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->removeCategory($categoryId); + } + + public function testCategoryAuthorizationGranted() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + + $categoryId = $this->manager->createCategory($projectId, 'Test'); + $this->assertNotFalse($categoryId); + + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-manager')); + $this->assertTrue($this->user->removeCategory($categoryId)); + } + + public function testColumnAuthorizationForbidden() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + + $columnId = $this->manager->addColumn($projectId, 'Test'); + $this->assertNotFalse($columnId); + + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->removeColumn($columnId); + } + + public function testColumnAuthorizationForbiddenBecauseNotProjectManager() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + + $columnId = $this->manager->addColumn($projectId, 'Test'); + $this->assertNotFalse($columnId); + + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-member')); + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->removeColumn($columnId); + } + + public function testColumnAuthorizationGranted() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + + $columnId = $this->manager->addColumn($projectId, 'Test'); + $this->assertNotFalse($columnId); + + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-manager')); + $this->assertTrue($this->user->removeColumn($columnId)); + } + + public function testCommentAuthorizationForbidden() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-viewer')); + + $taskId = $this->manager->createTask('My Task', $projectId); + $this->assertNotFalse($taskId); + + $commentId = $this->manager->createComment($taskId, $this->userUserId, 'My comment'); + $this->assertNotFalse($commentId); + + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->updateComment($commentId, 'something else'); + } + + public function testCommentAuthorizationGranted() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-member')); + + $taskId = $this->user->createTask('My Task', $projectId); + $this->assertNotFalse($taskId); + + $commentId = $this->user->createComment($taskId, $this->userUserId, 'My comment'); + $this->assertNotFalse($commentId); + + $this->assertTrue($this->user->updateComment($commentId, 'something else')); + } + + public function testSubtaskAuthorizationForbidden() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-viewer')); + + $taskId = $this->manager->createTask('My Task', $projectId); + $this->assertNotFalse($taskId); + + $subtaskId = $this->manager->createSubtask($taskId, 'My subtask'); + $this->assertNotFalse($subtaskId); + + $this->setExpectedException('JsonRPC\Exception\AccessDeniedException'); + $this->user->removeSubtask($subtaskId); + } + + public function testSubtaskAuthorizationGranted() + { + $projectId = $this->manager->createProject(array( + 'name' => 'Test Project', + 'owner_id' => $this->managerUserId, + )); + + $this->assertNotFalse($projectId); + $this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-member')); + + $taskId = $this->user->createTask('My Task', $projectId); + $this->assertNotFalse($taskId); + + $subtaskId = $this->manager->createSubtask($taskId, 'My subtask'); + $this->assertNotFalse($subtaskId); + + $this->assertTrue($this->user->removeSubtask($subtaskId)); + } +} diff --git a/tests/integration/ProjectPermissionProcedureTest.php b/tests/integration/ProjectPermissionProcedureTest.php new file mode 100644 index 00000000..74313dc4 --- /dev/null +++ b/tests/integration/ProjectPermissionProcedureTest.php @@ -0,0 +1,89 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class ProjectPermissionProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'Project with permission'; + protected $username = 'user-project-permission'; + protected $groupName1 = 'My group A for project permission'; + protected $groupName2 = 'My group B for project permission'; + + public function testAll() + { + $this->assertCreateTeamProject(); + $this->assertCreateGroups(); + $this->assertCreateUser(); + + $this->assertAddProjectUser(); + $this->assertGetProjectUsers(); + $this->assertGetAssignableUsers(); + $this->assertChangeProjectUserRole(); + $this->assertRemoveProjectUser(); + + $this->assertAddProjectGroup(); + $this->assertGetProjectUsers(); + $this->assertGetAssignableUsers(); + $this->assertChangeProjectGroupRole(); + $this->assertRemoveProjectGroup(); + } + + public function assertAddProjectUser() + { + $this->assertTrue($this->app->addProjectUser($this->projectId, $this->userId)); + } + + public function assertGetProjectUsers() + { + $members = $this->app->getProjectUsers($this->projectId); + $this->assertCount(1, $members); + $this->assertArrayHasKey($this->userId, $members); + $this->assertEquals($this->username, $members[$this->userId]); + } + + public function assertGetAssignableUsers() + { + $members = $this->app->getAssignableUsers($this->projectId); + $this->assertCount(1, $members); + $this->assertArrayHasKey($this->userId, $members); + $this->assertEquals($this->username, $members[$this->userId]); + } + + public function assertChangeProjectUserRole() + { + $this->assertTrue($this->app->changeProjectUserRole($this->projectId, $this->userId, 'project-viewer')); + + $members = $this->app->getAssignableUsers($this->projectId); + $this->assertCount(0, $members); + } + + public function assertRemoveProjectUser() + { + $this->assertTrue($this->app->removeProjectUser($this->projectId, $this->userId)); + + $members = $this->app->getProjectUsers($this->projectId); + $this->assertCount(0, $members); + } + + public function assertAddProjectGroup() + { + $this->assertTrue($this->app->addGroupMember($this->groupId1, $this->userId)); + $this->assertTrue($this->app->addProjectGroup($this->projectId, $this->groupId1)); + } + + public function assertChangeProjectGroupRole() + { + $this->assertTrue($this->app->changeProjectGroupRole($this->projectId, $this->groupId1, 'project-viewer')); + + $members = $this->app->getAssignableUsers($this->projectId); + $this->assertCount(0, $members); + } + + public function assertRemoveProjectGroup() + { + $this->assertTrue($this->app->removeProjectGroup($this->projectId, $this->groupId1)); + + $members = $this->app->getProjectUsers($this->projectId); + $this->assertCount(0, $members); + } +} diff --git a/tests/integration/ProjectPermissionTest.php b/tests/integration/ProjectPermissionTest.php deleted file mode 100644 index b06ad4ad..00000000 --- a/tests/integration/ProjectPermissionTest.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -require_once __DIR__.'/Base.php'; - -class ProjectPermissionTest extends Base -{ - public function testGetProjectUsers() - { - $this->assertNotFalse($this->app->createProject('Test')); - $this->assertNotFalse($this->app->createGroup('Test')); - - $projectId = $this->getProjectId(); - $groupId = $this->getGroupId(); - - $this->assertTrue($this->app->addGroupMember($projectId, $groupId)); - $this->assertSame(array(), $this->app->getProjectUsers($projectId)); - } - - public function testProjectUser() - { - $projectId = $this->getProjectId(); - $this->assertTrue($this->app->addProjectUser($projectId, 1)); - - $users = $this->app->getProjectUsers($projectId); - $this->assertCount(1, $users); - $this->assertEquals('admin', $users[1]); - - $users = $this->app->getAssignableUsers($projectId); - $this->assertCount(1, $users); - $this->assertEquals('admin', $users[1]); - - $this->assertTrue($this->app->changeProjectUserRole($projectId, 1, 'project-viewer')); - - $users = $this->app->getAssignableUsers($projectId); - $this->assertCount(0, $users); - - $this->assertTrue($this->app->removeProjectUser($projectId, 1)); - $this->assertSame(array(), $this->app->getProjectUsers($projectId)); - } - - public function testProjectGroup() - { - $projectId = $this->getProjectId(); - $groupId = $this->getGroupId(); - - $this->assertTrue($this->app->addProjectGroup($projectId, $groupId)); - - $users = $this->app->getProjectUsers($projectId); - $this->assertCount(1, $users); - $this->assertEquals('admin', $users[1]); - - $users = $this->app->getAssignableUsers($projectId); - $this->assertCount(1, $users); - $this->assertEquals('admin', $users[1]); - - $this->assertTrue($this->app->changeProjectGroupRole($projectId, $groupId, 'project-viewer')); - - $users = $this->app->getAssignableUsers($projectId); - $this->assertCount(0, $users); - - $this->assertTrue($this->app->removeProjectGroup($projectId, 1)); - $this->assertSame(array(), $this->app->getProjectUsers($projectId)); - } -} diff --git a/tests/integration/ProjectProcedureTest.php b/tests/integration/ProjectProcedureTest.php new file mode 100644 index 00000000..69c2464f --- /dev/null +++ b/tests/integration/ProjectProcedureTest.php @@ -0,0 +1,119 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class ProjectProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My team project'; + + public function testAll() + { + $this->assertCreateTeamProject(); + $this->assertGetProjectById(); + $this->assertGetProjectByName(); + $this->assertGetAllProjects(); + $this->assertUpdateProject(); + $this->assertUpdateProjectIdentifier(); + $this->assertCreateProjectWithIdentifier(); + $this->assertGetProjectActivity(); + $this->assertGetProjectsActivity(); + $this->assertEnableDisableProject(); + $this->assertEnableDisablePublicAccess(); + $this->assertRemoveProject(); + } + + public function assertGetProjectById() + { + $project = $this->app->getProjectById($this->projectId); + $this->assertNotNull($project); + $this->assertEquals($this->projectName, $project['name']); + $this->assertEquals('Description', $project['description']); + } + + public function assertGetProjectByName() + { + $project = $this->app->getProjectByName($this->projectName); + $this->assertNotNull($project); + $this->assertEquals($this->projectId, $project['id']); + $this->assertEquals($this->projectName, $project['name']); + $this->assertEquals('Description', $project['description']); + } + + public function assertGetAllProjects() + { + $projects = $this->app->getAllProjects(); + $this->assertNotEmpty($projects); + } + + public function assertGetProjectActivity() + { + $activities = $this->app->getProjectActivity($this->projectId); + $this->assertInternalType('array', $activities); + $this->assertCount(0, $activities); + } + + public function assertGetProjectsActivity() + { + $activities = $this->app->getProjectActivities(array('project_ids' => array($this->projectId))); + $this->assertInternalType('array', $activities); + $this->assertCount(0, $activities); + } + + public function assertUpdateProject() + { + $this->assertTrue($this->app->updateProject(array('project_id' => $this->projectId, 'name' => 'test', 'description' => 'test'))); + + $project = $this->app->getProjectById($this->projectId); + $this->assertNotNull($project); + $this->assertEquals('test', $project['name']); + $this->assertEquals('test', $project['description']); + + $this->assertTrue($this->app->updateProject(array('project_id' => $this->projectId, 'name' => $this->projectName))); + } + + public function assertUpdateProjectIdentifier() + { + $this->assertTrue($this->app->updateProject(array( + 'project_id' => $this->projectId, + 'identifier' => 'MYPROJECT', + ))); + + $project = $this->app->getProjectById($this->projectId); + $this->assertNotNull($project); + $this->assertEquals($this->projectName, $project['name']); + $this->assertEquals('MYPROJECT', $project['identifier']); + } + + public function assertCreateProjectWithIdentifier() + { + $projectId = $this->app->createProject(array( + 'name' => 'My project with an identifier', + 'identifier' => 'MYPROJECTWITHIDENTIFIER', + )); + + $this->assertNotFalse($projectId); + + $project = $this->app->getProjectByIdentifier('MYPROJECTWITHIDENTIFIER'); + $this->assertEquals($projectId, $project['id']); + $this->assertEquals('My project with an identifier', $project['name']); + $this->assertEquals('MYPROJECTWITHIDENTIFIER', $project['identifier']); + } + + public function assertEnableDisableProject() + { + $this->assertTrue($this->app->disableProject($this->projectId)); + $this->assertTrue($this->app->enableProject($this->projectId)); + } + + public function assertEnableDisablePublicAccess() + { + $this->assertTrue($this->app->disableProjectPublicAccess($this->projectId)); + $this->assertTrue($this->app->enableProjectPublicAccess($this->projectId)); + } + + public function assertRemoveProject() + { + $this->assertTrue($this->app->removeProject($this->projectId)); + $this->assertNull($this->app->getProjectById($this->projectId)); + } +} diff --git a/tests/integration/SubtaskProcedureTest.php b/tests/integration/SubtaskProcedureTest.php new file mode 100644 index 00000000..7ab4ef0b --- /dev/null +++ b/tests/integration/SubtaskProcedureTest.php @@ -0,0 +1,64 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class SubtaskProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test subtasks'; + private $subtaskId = 0; + + public function testAll() + { + $this->assertCreateTeamProject(); + $this->assertCreateTask(); + $this->assertCreateSubtask(); + $this->assertGetSubtask(); + $this->assertUpdateSubtask(); + $this->assertGetAllSubtasks(); + $this->assertRemoveSubtask(); + } + + public function assertCreateSubtask() + { + $this->subtaskId = $this->app->createSubtask(array( + 'task_id' => $this->taskId, + 'title' => 'subtask #1', + )); + + $this->assertNotFalse($this->subtaskId); + } + + public function assertGetSubtask() + { + $subtask = $this->app->getSubtask($this->subtaskId); + $this->assertEquals($this->taskId, $subtask['task_id']); + $this->assertEquals('subtask #1', $subtask['title']); + } + + public function assertUpdateSubtask() + { + $this->assertTrue($this->app->execute('updateSubtask', array( + 'id' => $this->subtaskId, + 'task_id' => $this->taskId, + 'title' => 'test', + ))); + + $subtask = $this->app->getSubtask($this->subtaskId); + $this->assertEquals('test', $subtask['title']); + } + + public function assertGetAllSubtasks() + { + $subtasks = $this->app->getAllSubtasks($this->taskId); + $this->assertCount(1, $subtasks); + $this->assertEquals('test', $subtasks[0]['title']); + } + + public function assertRemoveSubtask() + { + $this->assertTrue($this->app->removeSubtask($this->subtaskId)); + + $subtasks = $this->app->getAllSubtasks($this->taskId); + $this->assertCount(0, $subtasks); + } +} diff --git a/tests/integration/SwimlaneProcedureTest.php b/tests/integration/SwimlaneProcedureTest.php new file mode 100644 index 00000000..e64342b4 --- /dev/null +++ b/tests/integration/SwimlaneProcedureTest.php @@ -0,0 +1,93 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class SwimlaneProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test swimlanes'; + private $swimlaneId = 0; + + public function testAll() + { + $this->assertCreateTeamProject(); + } + + public function assertGetDefaultSwimlane() + { + $swimlane = $this->app->getDefaultSwimlane($this->projectId); + $this->assertNotEmpty($swimlane); + $this->assertEquals('Default swimlane', $swimlane['default_swimlane']); + } + + public function assertAddSwimlane() + { + $this->swimlaneId = $this->app->addSwimlane($this->projectId, 'Swimlane 1'); + $this->assertNotFalse($this->swimlaneId); + $this->assertNotFalse($this->app->addSwimlane($this->projectId, 'Swimlane 2')); + } + + public function assertGetSwimlane() + { + $swimlane = $this->app->getSwimlane($this->swimlaneId); + $this->assertInternalType('array', $swimlane); + $this->assertEquals('Swimlane 1', $swimlane['name']); + } + + public function assertUpdateSwimlane() + { + $this->assertTrue($this->app->updateSwimlane($this->swimlaneId, 'Another swimlane')); + + $swimlane = $this->app->getSwimlaneById($this->swimlaneId); + $this->assertEquals('Another swimlane', $swimlane['name']); + } + + public function assertDisableSwimlane() + { + $this->assertTrue($this->app->disableSwimlane($this->projectId, $this->swimlaneId)); + + $swimlane = $this->app->getSwimlaneById($this->swimlaneId); + $this->assertEquals(0, $swimlane['is_active']); + } + + public function assertEnableSwimlane() + { + $this->assertTrue($this->app->enableSwimlane($this->projectId, $this->swimlaneId)); + + $swimlane = $this->app->getSwimlaneById($this->swimlaneId); + $this->assertEquals(1, $swimlane['is_active']); + } + + public function assertGetAllSwimlanes() + { + $swimlanes = $this->app->getAllSwimlanes($this->projectId); + $this->assertCount(2, $swimlanes); + $this->assertEquals('Another swimlane', $swimlanes[0]['name']); + $this->assertEquals('Swimlane 2', $swimlanes[1]['name']); + } + + public function assertGetActiveSwimlane() + { + $this->assertTrue($this->app->disableSwimlane($this->projectId, $this->swimlaneId)); + + $swimlanes = $this->app->getActiveSwimlanes($this->projectId); + $this->assertCount(2, $swimlanes); + $this->assertEquals('Default swimlane', $swimlanes[0]['name']); + $this->assertEquals('Swimlane 2', $swimlanes[1]['name']); + } + + public function assertRemoveSwimlane() + { + $this->assertTrue($this->app->removeSwimlane($this->projectId, $this->swimlaneId)); + } + + public function assertChangePosition() + { + $swimlaneId1 = $this->app->addSwimlane($this->projectId, 'Swimlane A'); + $this->assertNotFalse($this->app->addSwimlane($this->projectId, 'Swimlane B')); + + $swimlanes = $this->app->getAllSwimlanes($this->projectId); + $this->assertCount(3, $swimlanes); + + $this->assertTrue($this->app->changeSwimlanePosition($this->projectId, $swimlaneId1, 3)); + } +} diff --git a/tests/integration/SwimlaneTest.php b/tests/integration/SwimlaneTest.php deleted file mode 100644 index 88747204..00000000 --- a/tests/integration/SwimlaneTest.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -require_once __DIR__.'/Base.php'; - -class SwimlaneTest extends Base -{ - public function testCreateProject() - { - $this->assertEquals(1, $this->app->createProject('A project')); - } - - public function testGetDefaultSwimlane() - { - $swimlane = $this->app->getDefaultSwimlane(1); - $this->assertNotEmpty($swimlane); - $this->assertEquals('Default swimlane', $swimlane['default_swimlane']); - } - - public function testAddSwimlane() - { - $swimlane_id = $this->app->addSwimlane(1, 'Swimlane 1'); - $this->assertNotFalse($swimlane_id); - $this->assertInternalType('int', $swimlane_id); - - $swimlane = $this->app->getSwimlaneById($swimlane_id); - $this->assertNotEmpty($swimlane); - $this->assertInternalType('array', $swimlane); - $this->assertEquals('Swimlane 1', $swimlane['name']); - } - - public function testGetSwimlane() - { - $swimlane = $this->app->getSwimlane(1); - $this->assertInternalType('array', $swimlane); - $this->assertEquals('Swimlane 1', $swimlane['name']); - } - - public function testUpdateSwimlane() - { - $swimlane = $this->app->getSwimlaneByName(1, 'Swimlane 1'); - $this->assertInternalType('array', $swimlane); - $this->assertEquals(1, $swimlane['id']); - $this->assertEquals('Swimlane 1', $swimlane['name']); - - $this->assertTrue($this->app->updateSwimlane($swimlane['id'], 'Another swimlane')); - - $swimlane = $this->app->getSwimlaneById($swimlane['id']); - $this->assertEquals('Another swimlane', $swimlane['name']); - } - - public function testDisableSwimlane() - { - $this->assertTrue($this->app->disableSwimlane(1, 1)); - - $swimlane = $this->app->getSwimlaneById(1); - $this->assertEquals(0, $swimlane['is_active']); - } - - public function testEnableSwimlane() - { - $this->assertTrue($this->app->enableSwimlane(1, 1)); - - $swimlane = $this->app->getSwimlaneById(1); - $this->assertEquals(1, $swimlane['is_active']); - } - - public function testGetAllSwimlanes() - { - $this->assertNotFalse($this->app->addSwimlane(1, 'Swimlane A')); - - $swimlanes = $this->app->getAllSwimlanes(1); - $this->assertCount(2, $swimlanes); - $this->assertEquals('Another swimlane', $swimlanes[0]['name']); - $this->assertEquals('Swimlane A', $swimlanes[1]['name']); - } - - public function testGetActiveSwimlane() - { - $this->assertTrue($this->app->disableSwimlane(1, 1)); - - $swimlanes = $this->app->getActiveSwimlanes(1); - $this->assertCount(2, $swimlanes); - $this->assertEquals('Default swimlane', $swimlanes[0]['name']); - $this->assertEquals('Swimlane A', $swimlanes[1]['name']); - } - - public function testRemoveSwimlane() - { - $this->assertTrue($this->app->removeSwimlane(1, 2)); - } - - public function testChangePosition() - { - $this->assertNotFalse($this->app->addSwimlane(1, 'Swimlane 1')); - $this->assertNotFalse($this->app->addSwimlane(1, 'Swimlane 2')); - - $swimlanes = $this->app->getAllSwimlanes(1); - $this->assertCount(3, $swimlanes); - - $this->assertTrue($this->app->changeSwimlanePosition(1, 1, 3)); - $this->assertFalse($this->app->changeSwimlanePosition(1, 1, 6)); - } -} diff --git a/tests/integration/TaskFileProcedureTest.php b/tests/integration/TaskFileProcedureTest.php new file mode 100644 index 00000000..61155555 --- /dev/null +++ b/tests/integration/TaskFileProcedureTest.php @@ -0,0 +1,67 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class TaskFileProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test task files'; + protected $fileId; + + public function testAll() + { + $this->assertCreateTeamProject(); + $this->assertCreateTask(); + $this->assertCreateTaskFile(); + $this->assertGetTaskFile(); + $this->assertDownloadTaskFile(); + $this->assertGetAllFiles(); + $this->assertRemoveTaskFile(); + $this->assertRemoveAllTaskFiles(); + } + + public function assertCreateTaskFile() + { + $this->fileId = $this->app->createTaskFile(1, $this->taskId, 'My file', base64_encode('plain text file')); + $this->assertNotFalse($this->fileId); + } + + public function assertGetTaskFile() + { + $file = $this->app->getTaskFile($this->fileId); + $this->assertNotEmpty($file); + $this->assertEquals('My file', $file['name']); + } + + public function assertDownloadTaskFile() + { + $content = $this->app->downloadTaskFile($this->fileId); + $this->assertNotEmpty($content); + $this->assertEquals('plain text file', base64_decode($content)); + } + + public function assertGetAllFiles() + { + $files = $this->app->getAllTaskFiles(array('task_id' => $this->taskId)); + $this->assertCount(1, $files); + $this->assertEquals('My file', $files[0]['name']); + } + + public function assertRemoveTaskFile() + { + $this->assertTrue($this->app->removeTaskFile($this->fileId)); + + $files = $this->app->getAllTaskFiles(array('task_id' => $this->taskId)); + $this->assertEmpty($files); + } + + public function assertRemoveAllTaskFiles() + { + $this->assertCreateTaskFile(); + $this->assertCreateTaskFile(); + + $this->assertTrue($this->app->removeAllTaskFiles($this->taskId)); + + $files = $this->app->getAllTaskFiles(array('task_id' => $this->taskId)); + $this->assertEmpty($files); + } +} diff --git a/tests/integration/TaskLinkProcedureTest.php b/tests/integration/TaskLinkProcedureTest.php new file mode 100644 index 00000000..a25fced5 --- /dev/null +++ b/tests/integration/TaskLinkProcedureTest.php @@ -0,0 +1,68 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class TaskLinkProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test task links'; + protected $taskLinkId; + protected $taskId1; + protected $taskId2; + + public function testAll() + { + $this->assertCreateTeamProject(); + + $this->taskId1 = $this->app->createTask(array('project_id' => $this->projectId, 'title' => 'Task 1')); + $this->taskId2 = $this->app->createTask(array('project_id' => $this->projectId, 'title' => 'Task 2')); + + $this->assertNotFalse($this->taskId1); + $this->assertNotFalse($this->taskId2); + + $this->assertCreateTaskLink(); + $this->assertGetTaskLink(); + $this->assertGetAllTaskLinks(); + $this->assertUpdateTaskLink(); + $this->assertRemoveTaskLink(); + } + + public function assertCreateTaskLink() + { + $this->taskLinkId = $this->app->createTaskLink($this->taskId1, $this->taskId2, 1); + $this->assertNotFalse($this->taskLinkId); + } + + public function assertGetTaskLink() + { + $link = $this->app->getTaskLinkById($this->taskLinkId); + $this->assertNotNull($link); + $this->assertEquals($this->taskId1, $link['task_id']); + $this->assertEquals($this->taskId2, $link['opposite_task_id']); + $this->assertEquals(1, $link['link_id']); + } + + public function assertGetAllTaskLinks() + { + $links = $this->app->getAllTaskLinks($this->taskId2); + $this->assertCount(1, $links); + } + + public function assertUpdateTaskLink() + { + $this->assertTrue($this->app->updateTaskLink($this->taskLinkId, $this->taskId1, $this->taskId2, 3)); + + $link = $this->app->getTaskLinkById($this->taskLinkId); + $this->assertNotNull($link); + $this->assertEquals($this->taskId1, $link['task_id']); + $this->assertEquals($this->taskId2, $link['opposite_task_id']); + $this->assertEquals(3, $link['link_id']); + } + + public function assertRemoveTaskLink() + { + $this->assertTrue($this->app->removeTaskLink($this->taskLinkId)); + + $links = $this->app->getAllTaskLinks($this->taskId2); + $this->assertCount(0, $links); + } +} diff --git a/tests/integration/TaskProcedureTest.php b/tests/integration/TaskProcedureTest.php new file mode 100644 index 00000000..f456ae52 --- /dev/null +++ b/tests/integration/TaskProcedureTest.php @@ -0,0 +1,55 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class TaskProcedureTest extends BaseProcedureTest +{ + protected $projectName = 'My project to test tasks'; + + public function testAll() + { + $this->assertCreateTeamProject(); + $this->assertCreateTask(); + $this->assertUpdateTask(); + $this->assertGetTaskById(); + $this->assertGetTaskByReference(); + $this->assertGetAllTasks(); + $this->assertOpenCloseTask(); + } + + public function assertUpdateTask() + { + $this->assertTrue($this->app->updateTask(array('id' => $this->taskId, 'color_id' => 'red'))); + } + + public function assertGetTaskById() + { + $task = $this->app->getTask($this->taskId); + $this->assertNotNull($task); + $this->assertEquals('red', $task['color_id']); + $this->assertEquals($this->taskTitle, $task['title']); + } + + public function assertGetTaskByReference() + { + $taskId = $this->app->createTask(array('title' => 'task with reference', 'project_id' => $this->projectId, 'reference' => 'test')); + $this->assertNotFalse($taskId); + + $task = $this->app->getTaskByReference($this->projectId, 'test'); + $this->assertNotNull($task); + $this->assertEquals($taskId, $task['id']); + } + + public function assertGetAllTasks() + { + $tasks = $this->app->getAllTasks($this->projectId); + $this->assertInternalType('array', $tasks); + $this->assertNotEmpty($tasks); + } + + public function assertOpenCloseTask() + { + $this->assertTrue($this->app->closeTask($this->taskId)); + $this->assertTrue($this->app->openTask($this->taskId)); + } +} diff --git a/tests/integration/TaskTest.php b/tests/integration/TaskTest.php deleted file mode 100644 index 0c398761..00000000 --- a/tests/integration/TaskTest.php +++ /dev/null @@ -1,132 +0,0 @@ -<?php - -require_once __DIR__.'/Base.php'; - -class TaskTest extends Base -{ - public function testSearchTasks() - { - $project_id1 = $this->app->createProject('My project'); - $project_id2 = $this->app->createProject('My project'); - $this->assertNotFalse($project_id1); - $this->assertNotFalse($project_id2); - - $this->assertNotFalse($this->app->createTask(array('project_id' => $project_id1, 'title' => 'T1'))); - $this->assertNotFalse($this->app->createTask(array('project_id' => $project_id1, 'title' => 'T2'))); - $this->assertNotFalse($this->app->createTask(array('project_id' => $project_id2, 'title' => 'T3'))); - - $tasks = $this->app->searchTasks($project_id1, 't2'); - $this->assertCount(1, $tasks); - $this->assertEquals('T2', $tasks[0]['title']); - - $tasks = $this->app->searchTasks(array('project_id' => $project_id2, 'query' => 'assignee:nobody')); - $this->assertCount(1, $tasks); - $this->assertEquals('T3', $tasks[0]['title']); - } - - public function testPriorityAttribute() - { - $project_id = $this->app->createProject('My project'); - $this->assertNotFalse($project_id); - - $task_id = $this->app->createTask(array('project_id' => $project_id, 'title' => 'My task', 'priority' => 2)); - - $task = $this->app->getTask($task_id); - $this->assertEquals(2, $task['priority']); - - $this->assertTrue($this->app->updateTask(array('id' => $task_id, 'project_id' => $project_id, 'priority' => 3))); - - $task = $this->app->getTask($task_id); - $this->assertEquals(3, $task['priority']); - } - - public function testChangeAssigneeToAssignableUser() - { - $project_id = $this->app->createProject('My project'); - $this->assertNotFalse($project_id); - - $user_id = $this->app->createUser('user0', 'password'); - $this->assertNotFalse($user_id); - - $this->assertTrue($this->app->addProjectUser($project_id, $user_id, 'project-member')); - - $task_id = $this->app->createTask(array('project_id' => $project_id, 'title' => 'My task')); - $this->assertNotFalse($task_id); - - $this->assertTrue($this->app->updateTask(array('id' => $task_id, 'project_id' => $project_id, 'owner_id' => $user_id))); - - $task = $this->app->getTask($task_id); - $this->assertEquals($user_id, $task['owner_id']); - } - - public function testChangeAssigneeToNotAssignableUser() - { - $project_id = $this->app->createProject('My project'); - $this->assertNotFalse($project_id); - - $task_id = $this->app->createTask(array('project_id' => $project_id, 'title' => 'My task')); - $this->assertNotFalse($task_id); - - $this->assertFalse($this->app->updateTask(array('id' => $task_id, 'project_id' => $project_id, 'owner_id' => 1))); - - $task = $this->app->getTask($task_id); - $this->assertEquals(0, $task['owner_id']); - } - - public function testChangeAssigneeToNobody() - { - $project_id = $this->app->createProject('My project'); - $this->assertNotFalse($project_id); - - $user_id = $this->app->createUser('user1', 'password'); - $this->assertNotFalse($user_id); - - $this->assertTrue($this->app->addProjectUser($project_id, $user_id, 'project-member')); - - $task_id = $this->app->createTask(array('project_id' => $project_id, 'title' => 'My task', 'owner_id' => $user_id)); - $this->assertNotFalse($task_id); - - $this->assertTrue($this->app->updateTask(array('id' => $task_id, 'project_id' => $project_id, 'owner_id' => 0))); - - $task = $this->app->getTask($task_id); - $this->assertEquals(0, $task['owner_id']); - } - - public function testMoveTaskToAnotherProject() - { - $project_id1 = $this->app->createProject('My project'); - $this->assertNotFalse($project_id1); - - $project_id2 = $this->app->createProject('My project'); - $this->assertNotFalse($project_id2); - - $task_id = $this->app->createTask(array('project_id' => $project_id1, 'title' => 'My task')); - $this->assertNotFalse($task_id); - - $this->assertTrue($this->app->moveTaskToProject($task_id, $project_id2)); - - $task = $this->app->getTask($task_id); - $this->assertEquals($project_id2, $task['project_id']); - } - - public function testMoveCopyToAnotherProject() - { - $project_id1 = $this->app->createProject('My project'); - $this->assertNotFalse($project_id1); - - $project_id2 = $this->app->createProject('My project'); - $this->assertNotFalse($project_id2); - - $task_id1 = $this->app->createTask(array('project_id' => $project_id1, 'title' => 'My task')); - $this->assertNotFalse($task_id1); - - $task_id2 = $this->app->duplicateTaskToProject($task_id1, $project_id2); - $this->assertNotFalse($task_id2); - - $task = $this->app->getTask($task_id1); - $this->assertEquals($project_id1, $task['project_id']); - - $task = $this->app->getTask($task_id2); - $this->assertEquals($project_id2, $task['project_id']); - } -} diff --git a/tests/integration/UserProcedureTest.php b/tests/integration/UserProcedureTest.php new file mode 100644 index 00000000..290f87fb --- /dev/null +++ b/tests/integration/UserProcedureTest.php @@ -0,0 +1,63 @@ +<?php + +require_once __DIR__.'/BaseProcedureTest.php'; + +class UserProcedureTest extends BaseProcedureTest +{ + public function testAll() + { + $this->assertCreateUser(); + $this->assertGetUserById(); + $this->assertGetUserByName(); + $this->assertGetAllUsers(); + $this->assertEnableDisableUser(); + $this->assertUpdateUser(); + $this->assertRemoveUser(); + } + + public function assertGetUserById() + { + $user = $this->app->getUser($this->userId); + $this->assertNotNull($user); + $this->assertEquals($this->username, $user['username']); + } + + public function assertGetUserByName() + { + $user = $this->app->getUserByName($this->username); + $this->assertNotNull($user); + $this->assertEquals($this->username, $user['username']); + } + + public function assertGetAllUsers() + { + $users = $this->app->getAllUsers(); + $this->assertInternalType('array', $users); + $this->assertNotEmpty($users); + } + + public function assertEnableDisableUser() + { + $this->assertTrue($this->app->disableUser($this->userId)); + $this->assertFalse($this->app->isActiveUser($this->userId)); + $this->assertTrue($this->app->enableUser($this->userId)); + $this->assertTrue($this->app->isActiveUser($this->userId)); + } + + public function assertUpdateUser() + { + $this->assertTrue($this->app->updateUser(array( + 'id' => $this->userId, + 'name' => 'My user', + ))); + + $user = $this->app->getUser($this->userId); + $this->assertNotNull($user); + $this->assertEquals('My user', $user['name']); + } + + public function assertRemoveUser() + { + $this->assertTrue($this->app->removeUser($this->userId)); + } +} diff --git a/tests/integration/UserTest.php b/tests/integration/UserTest.php deleted file mode 100644 index 10da051c..00000000 --- a/tests/integration/UserTest.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -require_once __DIR__.'/Base.php'; - -class UserTest extends Base -{ - public function testDisableUser() - { - $this->assertEquals(2, $this->app->createUser(array('username' => 'someone', 'password' => 'test123'))); - $this->assertTrue($this->app->isActiveUser(2)); - - $this->assertTrue($this->app->disableUser(2)); - $this->assertFalse($this->app->isActiveUser(2)); - - $this->assertTrue($this->app->enableUser(2)); - $this->assertTrue($this->app->isActiveUser(2)); - } -} diff --git a/tests/units/Model/ActionTest.php b/tests/units/Model/ActionModelTest.php index 5db18983..4e21a999 100644 --- a/tests/units/Model/ActionTest.php +++ b/tests/units/Model/ActionModelTest.php @@ -11,7 +11,7 @@ use Kanboard\Model\CategoryModel; use Kanboard\Model\ProjectUserRoleModel; use Kanboard\Core\Security\Role; -class ActionTest extends Base +class ActionModelTest extends Base { public function testCreate() { @@ -69,6 +69,24 @@ class ActionTest extends Base $this->assertEquals(array('column_id' => 1, 'color_id' => 'red'), $action['params']); } + public function testGetProjectId() + { + $projectModel = new ProjectModel($this->container); + $actionModel = new ActionModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + + $this->assertEquals(1, $actionModel->create(array( + 'project_id' => 1, + 'event_name' => TaskModel::EVENT_CREATE, + 'action_name' => '\Kanboard\Action\TaskAssignColorColumn', + 'params' => array('column_id' => 1, 'color_id' => 'red'), + ))); + + $this->assertEquals(1, $actionModel->getProjectId(1)); + $this->assertSame(0, $actionModel->getProjectId(42)); + } + public function testGetAll() { $projectModel = new ProjectModel($this->container); diff --git a/tests/units/Model/CategoryTest.php b/tests/units/Model/CategoryModelTest.php index 1fdc51f6..80a20af6 100644 --- a/tests/units/Model/CategoryTest.php +++ b/tests/units/Model/CategoryModelTest.php @@ -8,7 +8,7 @@ use Kanboard\Model\TaskFinderModel; use Kanboard\Model\ProjectModel; use Kanboard\Model\CategoryModel; -class CategoryTest extends Base +class CategoryModelTest extends Base { public function testCreation() { @@ -81,6 +81,18 @@ class CategoryTest extends Base $this->assertSame(0, $categoryModel->getIdByName(1, 'Category #2')); } + public function testGetProjectId() + { + $projectModel = new ProjectModel($this->container); + $categoryModel = new CategoryModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Project #1'))); + $this->assertEquals(1, $categoryModel->create(array('name' => 'Category #1', 'project_id' => 1, 'description' => 'test'))); + + $this->assertEquals(1, $categoryModel->getProjectId(1)); + $this->assertSame(0, $categoryModel->getProjectId(2)); + } + public function testGetList() { $projectModel = new ProjectModel($this->container); diff --git a/tests/units/Model/CommentTest.php b/tests/units/Model/CommentTest.php index 7250ae0b..574b5a87 100644 --- a/tests/units/Model/CommentTest.php +++ b/tests/units/Model/CommentTest.php @@ -10,16 +10,16 @@ class CommentTest extends Base { public function testCreate() { - $c = new CommentModel($this->container); - $tc = new TaskCreationModel($this->container); - $p = new ProjectModel($this->container); + $commentModel = new CommentModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $projectModel = new ProjectModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'test1'))); - $this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); - $this->assertEquals(1, $c->create(array('task_id' => 1, 'comment' => 'bla bla', 'user_id' => 1))); - $this->assertEquals(2, $c->create(array('task_id' => 1, 'comment' => 'bla bla'))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); + $this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'bla bla', 'user_id' => 1))); + $this->assertEquals(2, $commentModel->create(array('task_id' => 1, 'comment' => 'bla bla'))); - $comment = $c->getById(1); + $comment = $commentModel->getById(1); $this->assertNotEmpty($comment); $this->assertEquals('bla bla', $comment['comment']); $this->assertEquals(1, $comment['task_id']); @@ -27,7 +27,7 @@ class CommentTest extends Base $this->assertEquals('admin', $comment['username']); $this->assertEquals(time(), $comment['date_creation'], '', 3); - $comment = $c->getById(2); + $comment = $commentModel->getById(2); $this->assertNotEmpty($comment); $this->assertEquals('bla bla', $comment['comment']); $this->assertEquals(1, $comment['task_id']); @@ -38,17 +38,17 @@ class CommentTest extends Base public function testGetAll() { - $c = new CommentModel($this->container); - $tc = new TaskCreationModel($this->container); - $p = new ProjectModel($this->container); + $commentModel = new CommentModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $projectModel = new ProjectModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'test1'))); - $this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); - $this->assertNotFalse($c->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1))); - $this->assertNotFalse($c->create(array('task_id' => 1, 'comment' => 'c2', 'user_id' => 1))); - $this->assertNotFalse($c->create(array('task_id' => 1, 'comment' => 'c3', 'user_id' => 1))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); + $this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1))); + $this->assertEquals(2, $commentModel->create(array('task_id' => 1, 'comment' => 'c2', 'user_id' => 1))); + $this->assertEquals(3, $commentModel->create(array('task_id' => 1, 'comment' => 'c3', 'user_id' => 1))); - $comments = $c->getAll(1); + $comments = $commentModel->getAll(1); $this->assertNotEmpty($comments); $this->assertEquals(3, count($comments)); @@ -56,37 +56,51 @@ class CommentTest extends Base $this->assertEquals(2, $comments[1]['id']); $this->assertEquals(3, $comments[2]['id']); - $this->assertEquals(3, $c->count(1)); + $this->assertEquals(3, $commentModel->count(1)); } public function testUpdate() { - $c = new CommentModel($this->container); - $tc = new TaskCreationModel($this->container); - $p = new ProjectModel($this->container); + $commentModel = new CommentModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $projectModel = new ProjectModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'test1'))); - $this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); - $this->assertNotFalse($c->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1))); - $this->assertTrue($c->update(array('id' => 1, 'comment' => 'bla'))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); + $this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1))); + $this->assertTrue($commentModel->update(array('id' => 1, 'comment' => 'bla'))); - $comment = $c->getById(1); + $comment = $commentModel->getById(1); $this->assertNotEmpty($comment); $this->assertEquals('bla', $comment['comment']); } public function validateRemove() { - $c = new CommentModel($this->container); - $tc = new TaskCreationModel($this->container); - $p = new ProjectModel($this->container); + $commentModel = new CommentModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $projectModel = new ProjectModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'test1'))); - $this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); - $this->assertTrue($c->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); + $this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1))); - $this->assertTrue($c->remove(1)); - $this->assertFalse($c->remove(1)); - $this->assertFalse($c->remove(1111)); + $this->assertTrue($commentModel->remove(1)); + $this->assertFalse($commentModel->remove(1)); + $this->assertFalse($commentModel->remove(1111)); + } + + public function testGetProjectId() + { + $commentModel = new CommentModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $projectModel = new ProjectModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); + $this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1))); + + $this->assertEquals(1, $commentModel->getProjectId(1)); + $this->assertSame(0, $commentModel->getProjectId(2)); } } diff --git a/tests/units/Model/SubtaskTest.php b/tests/units/Model/SubtaskModelTest.php index b65ee609..6451189d 100644 --- a/tests/units/Model/SubtaskTest.php +++ b/tests/units/Model/SubtaskModelTest.php @@ -5,10 +5,9 @@ require_once __DIR__.'/../Base.php'; use Kanboard\Model\TaskCreationModel; use Kanboard\Model\SubtaskModel; use Kanboard\Model\ProjectModel; -use Kanboard\Core\User\UserSession; use Kanboard\Model\TaskFinderModel; -class SubtaskTest extends Base +class SubtaskModelTest extends Base { public function onSubtaskCreated($event) { @@ -70,18 +69,18 @@ class SubtaskTest extends Base public function testCreation() { - $tc = new TaskCreationModel($this->container); - $s = new SubtaskModel($this->container); - $p = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $projectModel = new ProjectModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'test'))); - $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1))); $this->container['dispatcher']->addListener(SubtaskModel::EVENT_CREATE, array($this, 'onSubtaskCreated')); - $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); + $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1))); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); $this->assertEquals(1, $subtask['id']); $this->assertEquals(1, $subtask['task_id']); @@ -95,19 +94,19 @@ class SubtaskTest extends Base public function testModification() { - $tc = new TaskCreationModel($this->container); - $s = new SubtaskModel($this->container); - $p = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $projectModel = new ProjectModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'test'))); - $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1))); $this->container['dispatcher']->addListener(SubtaskModel::EVENT_UPDATE, array($this, 'onSubtaskUpdated')); - $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); - $this->assertTrue($s->update(array('id' => 1, 'user_id' => 1, 'status' => SubtaskModel::STATUS_INPROGRESS))); + $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1))); + $this->assertTrue($subtaskModel->update(array('id' => 1, 'user_id' => 1, 'status' => SubtaskModel::STATUS_INPROGRESS))); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); $this->assertEquals(1, $subtask['id']); $this->assertEquals(1, $subtask['task_id']); @@ -121,61 +120,61 @@ class SubtaskTest extends Base public function testRemove() { - $tc = new TaskCreationModel($this->container); - $s = new SubtaskModel($this->container); - $p = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $projectModel = new ProjectModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'test'))); - $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); - $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1))); + $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1))); $this->container['dispatcher']->addListener(SubtaskModel::EVENT_DELETE, array($this, 'onSubtaskDeleted')); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); - $this->assertTrue($s->remove(1)); + $this->assertTrue($subtaskModel->remove(1)); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertEmpty($subtask); } public function testToggleStatusWithoutSession() { - $tc = new TaskCreationModel($this->container); - $s = new SubtaskModel($this->container); - $p = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $projectModel = new ProjectModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'test1'))); - $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1))); - $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); + $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1))); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); $this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']); $this->assertEquals(0, $subtask['user_id']); $this->assertEquals(1, $subtask['task_id']); - $this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $s->toggleStatus(1)); + $this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtaskModel->toggleStatus(1)); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); $this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtask['status']); $this->assertEquals(0, $subtask['user_id']); $this->assertEquals(1, $subtask['task_id']); - $this->assertEquals(SubtaskModel::STATUS_DONE, $s->toggleStatus(1)); + $this->assertEquals(SubtaskModel::STATUS_DONE, $subtaskModel->toggleStatus(1)); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); $this->assertEquals(SubtaskModel::STATUS_DONE, $subtask['status']); $this->assertEquals(0, $subtask['user_id']); $this->assertEquals(1, $subtask['task_id']); - $this->assertEquals(SubtaskModel::STATUS_TODO, $s->toggleStatus(1)); + $this->assertEquals(SubtaskModel::STATUS_TODO, $subtaskModel->toggleStatus(1)); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); $this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']); $this->assertEquals(0, $subtask['user_id']); @@ -184,17 +183,16 @@ class SubtaskTest extends Base public function testToggleStatusWithSession() { - $tc = new TaskCreationModel($this->container); - $s = new SubtaskModel($this->container); - $p = new ProjectModel($this->container); - $us = new UserSession($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $projectModel = new ProjectModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'test1'))); - $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1))); - $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); + $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1))); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); $this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']); $this->assertEquals(0, $subtask['user_id']); @@ -203,25 +201,25 @@ class SubtaskTest extends Base // Set the current logged user $this->container['sessionStorage']->user = array('id' => 1); - $this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $s->toggleStatus(1)); + $this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtaskModel->toggleStatus(1)); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); $this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtask['status']); $this->assertEquals(1, $subtask['user_id']); $this->assertEquals(1, $subtask['task_id']); - $this->assertEquals(SubtaskModel::STATUS_DONE, $s->toggleStatus(1)); + $this->assertEquals(SubtaskModel::STATUS_DONE, $subtaskModel->toggleStatus(1)); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); $this->assertEquals(SubtaskModel::STATUS_DONE, $subtask['status']); $this->assertEquals(1, $subtask['user_id']); $this->assertEquals(1, $subtask['task_id']); - $this->assertEquals(SubtaskModel::STATUS_TODO, $s->toggleStatus(1)); + $this->assertEquals(SubtaskModel::STATUS_TODO, $subtaskModel->toggleStatus(1)); - $subtask = $s->getById(1); + $subtask = $subtaskModel->getById(1); $this->assertNotEmpty($subtask); $this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']); $this->assertEquals(1, $subtask['user_id']); @@ -230,19 +228,19 @@ class SubtaskTest extends Base public function testCloseAll() { - $tc = new TaskCreationModel($this->container); - $s = new SubtaskModel($this->container); - $p = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $projectModel = new ProjectModel($this->container); - $this->assertEquals(1, $p->create(array('name' => 'test1'))); - $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1))); - $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); - $this->assertEquals(2, $s->create(array('title' => 'subtask #2', 'task_id' => 1))); + $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1))); + $this->assertEquals(2, $subtaskModel->create(array('title' => 'subtask #2', 'task_id' => 1))); - $this->assertTrue($s->closeAll(1)); + $this->assertTrue($subtaskModel->closeAll(1)); - $subtasks = $s->getAll(1); + $subtasks = $subtaskModel->getAll(1); $this->assertNotEmpty($subtasks); foreach ($subtasks as $subtask) { @@ -252,24 +250,24 @@ class SubtaskTest extends Base public function testDuplicate() { - $tc = new TaskCreationModel($this->container); - $s = new SubtaskModel($this->container); - $p = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $projectModel = new ProjectModel($this->container); // We create a project - $this->assertEquals(1, $p->create(array('name' => 'test1'))); + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); // We create 2 tasks - $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1))); - $this->assertEquals(2, $tc->create(array('title' => 'test 2', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 0))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1))); + $this->assertEquals(2, $taskCreationModel->create(array('title' => 'test 2', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 0))); // We create many subtasks for the first task - $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1, 'time_estimated' => 5, 'time_spent' => 3, 'status' => 1, 'another_subtask' => 'on'))); - $this->assertEquals(2, $s->create(array('title' => 'subtask #2', 'task_id' => 1, 'time_estimated' => '', 'time_spent' => '', 'status' => 2, 'user_id' => 1))); + $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1, 'time_estimated' => 5, 'time_spent' => 3, 'status' => 1, 'another_subtask' => 'on'))); + $this->assertEquals(2, $subtaskModel->create(array('title' => 'subtask #2', 'task_id' => 1, 'time_estimated' => '', 'time_spent' => '', 'status' => 2, 'user_id' => 1))); // We duplicate our subtasks - $this->assertTrue($s->duplicate(1, 2)); - $subtasks = $s->getAll(2); + $this->assertTrue($subtaskModel->duplicate(1, 2)); + $subtasks = $subtaskModel->getAll(2); $this->assertNotFalse($subtasks); $this->assertNotEmpty($subtasks); @@ -385,4 +383,18 @@ class SubtaskTest extends Base $this->assertEquals(2, $task['time_spent']); $this->assertEquals(3, $task['time_estimated']); } + + public function testGetProjectId() + { + $taskCreationModel = new TaskCreationModel($this->container); + $subtaskModel = new SubtaskModel($this->container); + $projectModel = new ProjectModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1))); + $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1))); + + $this->assertEquals(1, $subtaskModel->getProjectId(1)); + $this->assertEquals(0, $subtaskModel->getProjectId(2)); + } } diff --git a/tests/units/Model/TaskFileTest.php b/tests/units/Model/TaskFileModelTest.php index 2faee95c..de12553f 100644 --- a/tests/units/Model/TaskFileTest.php +++ b/tests/units/Model/TaskFileModelTest.php @@ -6,7 +6,7 @@ use Kanboard\Model\TaskFileModel; use Kanboard\Model\TaskCreationModel; use Kanboard\Model\ProjectModel; -class TaskFileTest extends Base +class TaskFileModelTest extends Base { public function testCreation() { @@ -442,4 +442,17 @@ class TaskFileTest extends Base $this->assertTrue($fileModel->removeAll(1)); } + + public function testGetProjectId() + { + $projectModel = new ProjectModel($this->container); + $fileModel = new TaskFileModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test'))); + $this->assertEquals(1, $fileModel->create(1, 'test', '/tmp/foobar', 10)); + $this->assertEquals(1, $fileModel->getProjectId(1)); + $this->assertEquals(0, $fileModel->getProjectId(2)); + } } diff --git a/tests/units/Model/TaskLinkModelTest.php b/tests/units/Model/TaskLinkModelTest.php new file mode 100644 index 00000000..78590891 --- /dev/null +++ b/tests/units/Model/TaskLinkModelTest.php @@ -0,0 +1,211 @@ +<?php + +require_once __DIR__.'/../Base.php'; + +use Kanboard\Model\TaskFinderModel; +use Kanboard\Model\TaskLinkModel; +use Kanboard\Model\TaskCreationModel; +use Kanboard\Model\ProjectModel; + +class TaskLinkModelTest extends Base +{ + // Check postgres issue: "Cardinality violation: 7 ERROR: more than one row returned by a subquery used as an expression" + public function testGetTaskWithMultipleMilestoneLink() + { + $taskFinderModel = new TaskFinderModel($this->container); + $taskLinkModel = new TaskLinkModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B'))); + $this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'C'))); + + $this->assertNotFalse($taskLinkModel->create(1, 2, 9)); + $this->assertNotFalse($taskLinkModel->create(1, 3, 9)); + + $task = $taskFinderModel->getExtendedQuery()->findOne(); + $this->assertNotEmpty($task); + } + + public function testCreateTaskLinkWithNoOpposite() + { + $taskLinkModel = new TaskLinkModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B'))); + $this->assertEquals(1, $taskLinkModel->create(1, 2, 1)); + + $links = $taskLinkModel->getAll(1); + $this->assertNotEmpty($links); + $this->assertCount(1, $links); + $this->assertEquals('relates to', $links[0]['label']); + $this->assertEquals('B', $links[0]['title']); + $this->assertEquals(2, $links[0]['task_id']); + $this->assertEquals(1, $links[0]['is_active']); + + $links = $taskLinkModel->getAll(2); + $this->assertNotEmpty($links); + $this->assertCount(1, $links); + $this->assertEquals('relates to', $links[0]['label']); + $this->assertEquals('A', $links[0]['title']); + $this->assertEquals(1, $links[0]['task_id']); + $this->assertEquals(1, $links[0]['is_active']); + + $task_link = $taskLinkModel->getById(1); + $this->assertNotEmpty($task_link); + $this->assertEquals(1, $task_link['id']); + $this->assertEquals(1, $task_link['task_id']); + $this->assertEquals(2, $task_link['opposite_task_id']); + $this->assertEquals(1, $task_link['link_id']); + + $opposite_task_link = $taskLinkModel->getOppositeTaskLink($task_link); + $this->assertNotEmpty($opposite_task_link); + $this->assertEquals(2, $opposite_task_link['id']); + $this->assertEquals(2, $opposite_task_link['task_id']); + $this->assertEquals(1, $opposite_task_link['opposite_task_id']); + $this->assertEquals(1, $opposite_task_link['link_id']); + } + + public function testCreateTaskLinkWithOpposite() + { + $taskLinkModel = new TaskLinkModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B'))); + $this->assertEquals(1, $taskLinkModel->create(1, 2, 2)); + + $links = $taskLinkModel->getAll(1); + $this->assertNotEmpty($links); + $this->assertCount(1, $links); + $this->assertEquals('blocks', $links[0]['label']); + $this->assertEquals('B', $links[0]['title']); + $this->assertEquals(2, $links[0]['task_id']); + $this->assertEquals(1, $links[0]['is_active']); + + $links = $taskLinkModel->getAll(2); + $this->assertNotEmpty($links); + $this->assertCount(1, $links); + $this->assertEquals('is blocked by', $links[0]['label']); + $this->assertEquals('A', $links[0]['title']); + $this->assertEquals(1, $links[0]['task_id']); + $this->assertEquals(1, $links[0]['is_active']); + + $task_link = $taskLinkModel->getById(1); + $this->assertNotEmpty($task_link); + $this->assertEquals(1, $task_link['id']); + $this->assertEquals(1, $task_link['task_id']); + $this->assertEquals(2, $task_link['opposite_task_id']); + $this->assertEquals(2, $task_link['link_id']); + + $opposite_task_link = $taskLinkModel->getOppositeTaskLink($task_link); + $this->assertNotEmpty($opposite_task_link); + $this->assertEquals(2, $opposite_task_link['id']); + $this->assertEquals(2, $opposite_task_link['task_id']); + $this->assertEquals(1, $opposite_task_link['opposite_task_id']); + $this->assertEquals(3, $opposite_task_link['link_id']); + } + + public function testGroupByLabel() + { + $taskLinkModel = new TaskLinkModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B'))); + $this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'C'))); + + $this->assertNotFalse($taskLinkModel->create(1, 2, 2)); + $this->assertNotFalse($taskLinkModel->create(1, 3, 2)); + + $links = $taskLinkModel->getAllGroupedByLabel(1); + $this->assertCount(1, $links); + $this->assertArrayHasKey('blocks', $links); + $this->assertCount(2, $links['blocks']); + $this->assertEquals('test', $links['blocks'][0]['project_name']); + $this->assertEquals('Backlog', $links['blocks'][0]['column_title']); + $this->assertEquals('blocks', $links['blocks'][0]['label']); + } + + public function testUpdate() + { + $taskLinkModel = new TaskLinkModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test1'))); + $this->assertEquals(2, $projectModel->create(array('name' => 'test2'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 2, 'title' => 'B'))); + $this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'C'))); + + $this->assertEquals(1, $taskLinkModel->create(1, 2, 5)); + $this->assertTrue($taskLinkModel->update(1, 1, 3, 11)); + + $links = $taskLinkModel->getAll(1); + $this->assertNotEmpty($links); + $this->assertCount(1, $links); + $this->assertEquals('is fixed by', $links[0]['label']); + $this->assertEquals('C', $links[0]['title']); + $this->assertEquals(3, $links[0]['task_id']); + + $links = $taskLinkModel->getAll(2); + $this->assertEmpty($links); + + $links = $taskLinkModel->getAll(3); + $this->assertNotEmpty($links); + $this->assertCount(1, $links); + $this->assertEquals('fixes', $links[0]['label']); + $this->assertEquals('A', $links[0]['title']); + $this->assertEquals(1, $links[0]['task_id']); + } + + public function testRemove() + { + $taskLinkModel = new TaskLinkModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B'))); + $this->assertEquals(1, $taskLinkModel->create(1, 2, 2)); + + $links = $taskLinkModel->getAll(1); + $this->assertNotEmpty($links); + $links = $taskLinkModel->getAll(2); + $this->assertNotEmpty($links); + + $this->assertTrue($taskLinkModel->remove($links[0]['id'])); + + $links = $taskLinkModel->getAll(1); + $this->assertEmpty($links); + $links = $taskLinkModel->getAll(2); + $this->assertEmpty($links); + } + + public function testGetProjectId() + { + $taskLinkModel = new TaskLinkModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B'))); + $this->assertEquals(1, $taskLinkModel->create(1, 2, 2)); + + $this->assertEquals(1, $taskLinkModel->getProjectId(1)); + $this->assertEquals(0, $taskLinkModel->getProjectId(42)); + } +} diff --git a/tests/units/Model/TaskLinkTest.php b/tests/units/Model/TaskLinkTest.php deleted file mode 100644 index bc574731..00000000 --- a/tests/units/Model/TaskLinkTest.php +++ /dev/null @@ -1,196 +0,0 @@ -<?php - -require_once __DIR__.'/../Base.php'; - -use Kanboard\Model\TaskFinderModel; -use Kanboard\Model\TaskLinkModel; -use Kanboard\Model\TaskCreationModel; -use Kanboard\Model\ProjectModel; - -class TaskLinkTest extends Base -{ - // Check postgres issue: "Cardinality violation: 7 ERROR: more than one row returned by a subquery used as an expression" - public function testGetTaskWithMultipleMilestoneLink() - { - $tf = new TaskFinderModel($this->container); - $tl = new TaskLinkModel($this->container); - $p = new ProjectModel($this->container); - $tc = new TaskCreationModel($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'test'))); - $this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A'))); - $this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B'))); - $this->assertEquals(3, $tc->create(array('project_id' => 1, 'title' => 'C'))); - - $this->assertNotFalse($tl->create(1, 2, 9)); - $this->assertNotFalse($tl->create(1, 3, 9)); - - $task = $tf->getExtendedQuery()->findOne(); - $this->assertNotEmpty($task); - } - - public function testCreateTaskLinkWithNoOpposite() - { - $tl = new TaskLinkModel($this->container); - $p = new ProjectModel($this->container); - $tc = new TaskCreationModel($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'test'))); - $this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A'))); - $this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B'))); - $this->assertEquals(1, $tl->create(1, 2, 1)); - - $links = $tl->getAll(1); - $this->assertNotEmpty($links); - $this->assertCount(1, $links); - $this->assertEquals('relates to', $links[0]['label']); - $this->assertEquals('B', $links[0]['title']); - $this->assertEquals(2, $links[0]['task_id']); - $this->assertEquals(1, $links[0]['is_active']); - - $links = $tl->getAll(2); - $this->assertNotEmpty($links); - $this->assertCount(1, $links); - $this->assertEquals('relates to', $links[0]['label']); - $this->assertEquals('A', $links[0]['title']); - $this->assertEquals(1, $links[0]['task_id']); - $this->assertEquals(1, $links[0]['is_active']); - - $task_link = $tl->getById(1); - $this->assertNotEmpty($task_link); - $this->assertEquals(1, $task_link['id']); - $this->assertEquals(1, $task_link['task_id']); - $this->assertEquals(2, $task_link['opposite_task_id']); - $this->assertEquals(1, $task_link['link_id']); - - $opposite_task_link = $tl->getOppositeTaskLink($task_link); - $this->assertNotEmpty($opposite_task_link); - $this->assertEquals(2, $opposite_task_link['id']); - $this->assertEquals(2, $opposite_task_link['task_id']); - $this->assertEquals(1, $opposite_task_link['opposite_task_id']); - $this->assertEquals(1, $opposite_task_link['link_id']); - } - - public function testCreateTaskLinkWithOpposite() - { - $tl = new TaskLinkModel($this->container); - $p = new ProjectModel($this->container); - $tc = new TaskCreationModel($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'test'))); - $this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A'))); - $this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B'))); - $this->assertEquals(1, $tl->create(1, 2, 2)); - - $links = $tl->getAll(1); - $this->assertNotEmpty($links); - $this->assertCount(1, $links); - $this->assertEquals('blocks', $links[0]['label']); - $this->assertEquals('B', $links[0]['title']); - $this->assertEquals(2, $links[0]['task_id']); - $this->assertEquals(1, $links[0]['is_active']); - - $links = $tl->getAll(2); - $this->assertNotEmpty($links); - $this->assertCount(1, $links); - $this->assertEquals('is blocked by', $links[0]['label']); - $this->assertEquals('A', $links[0]['title']); - $this->assertEquals(1, $links[0]['task_id']); - $this->assertEquals(1, $links[0]['is_active']); - - $task_link = $tl->getById(1); - $this->assertNotEmpty($task_link); - $this->assertEquals(1, $task_link['id']); - $this->assertEquals(1, $task_link['task_id']); - $this->assertEquals(2, $task_link['opposite_task_id']); - $this->assertEquals(2, $task_link['link_id']); - - $opposite_task_link = $tl->getOppositeTaskLink($task_link); - $this->assertNotEmpty($opposite_task_link); - $this->assertEquals(2, $opposite_task_link['id']); - $this->assertEquals(2, $opposite_task_link['task_id']); - $this->assertEquals(1, $opposite_task_link['opposite_task_id']); - $this->assertEquals(3, $opposite_task_link['link_id']); - } - - public function testGroupByLabel() - { - $tl = new TaskLinkModel($this->container); - $p = new ProjectModel($this->container); - $tc = new TaskCreationModel($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'test'))); - - $this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A'))); - $this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B'))); - $this->assertEquals(3, $tc->create(array('project_id' => 1, 'title' => 'C'))); - - $this->assertNotFalse($tl->create(1, 2, 2)); - $this->assertNotFalse($tl->create(1, 3, 2)); - - $links = $tl->getAllGroupedByLabel(1); - $this->assertCount(1, $links); - $this->assertArrayHasKey('blocks', $links); - $this->assertCount(2, $links['blocks']); - $this->assertEquals('test', $links['blocks'][0]['project_name']); - $this->assertEquals('Backlog', $links['blocks'][0]['column_title']); - $this->assertEquals('blocks', $links['blocks'][0]['label']); - } - - public function testUpdate() - { - $tl = new TaskLinkModel($this->container); - $p = new ProjectModel($this->container); - $tc = new TaskCreationModel($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'test1'))); - $this->assertEquals(2, $p->create(array('name' => 'test2'))); - $this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A'))); - $this->assertEquals(2, $tc->create(array('project_id' => 2, 'title' => 'B'))); - $this->assertEquals(3, $tc->create(array('project_id' => 1, 'title' => 'C'))); - - $this->assertEquals(1, $tl->create(1, 2, 5)); - $this->assertTrue($tl->update(1, 1, 3, 11)); - - $links = $tl->getAll(1); - $this->assertNotEmpty($links); - $this->assertCount(1, $links); - $this->assertEquals('is fixed by', $links[0]['label']); - $this->assertEquals('C', $links[0]['title']); - $this->assertEquals(3, $links[0]['task_id']); - - $links = $tl->getAll(2); - $this->assertEmpty($links); - - $links = $tl->getAll(3); - $this->assertNotEmpty($links); - $this->assertCount(1, $links); - $this->assertEquals('fixes', $links[0]['label']); - $this->assertEquals('A', $links[0]['title']); - $this->assertEquals(1, $links[0]['task_id']); - } - - public function testRemove() - { - $tl = new TaskLinkModel($this->container); - $p = new ProjectModel($this->container); - $tc = new TaskCreationModel($this->container); - - $this->assertEquals(1, $p->create(array('name' => 'test'))); - $this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A'))); - $this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B'))); - $this->assertEquals(1, $tl->create(1, 2, 2)); - - $links = $tl->getAll(1); - $this->assertNotEmpty($links); - $links = $tl->getAll(2); - $this->assertNotEmpty($links); - - $this->assertTrue($tl->remove($links[0]['id'])); - - $links = $tl->getAll(1); - $this->assertEmpty($links); - $links = $tl->getAll(2); - $this->assertEmpty($links); - } -} diff --git a/tests/units/Validator/ProjectValidatorTest.php b/tests/units/Validator/ProjectValidatorTest.php index 07de6c25..e1e2f077 100644 --- a/tests/units/Validator/ProjectValidatorTest.php +++ b/tests/units/Validator/ProjectValidatorTest.php @@ -55,13 +55,19 @@ class ProjectValidatorTest extends Base $r = $validator->validateModification(array('id' => 1, 'name' => 'test', 'identifier' => 'TEST1')); $this->assertTrue($r[0]); - $r = $validator->validateModification(array('id' => 1, 'name' => 'test', 'identifier' => 'test3')); + $r = $validator->validateModification(array('id' => 1, 'identifier' => 'test3')); $this->assertTrue($r[0]); - $r = $validator->validateModification(array('id' => 1, 'name' => 'test', 'identifier' => '')); + $r = $validator->validateModification(array('id' => 1, 'identifier' => '')); $this->assertTrue($r[0]); - $r = $validator->validateModification(array('id' => 1, 'name' => 'test', 'identifier' => 'TEST2')); + $r = $validator->validateModification(array('id' => 1, 'identifier' => 'TEST2')); + $this->assertFalse($r[0]); + + $r = $validator->validateModification(array('id' => 1, 'name' => '')); + $this->assertFalse($r[0]); + + $r = $validator->validateModification(array('id' => 1, 'name' => null)); $this->assertFalse($r[0]); } } |