Add new API procedures for groups, roles and project permissions

This commit is contained in:
Frederic Guillot
2016-01-22 21:23:12 -05:00
parent f27bcec2d9
commit ad8fcf035a
26 changed files with 1122 additions and 266 deletions

View File

@@ -6,6 +6,7 @@ New features:
* Forgot Password * Forgot Password
* Add dropdown menu on each board column title to close all tasks * Add dropdown menu on each board column title to close all tasks
* Add Malay language * Add Malay language
* Add new API procedures for groups, roles and project permissions
Improvements: Improvements:

View File

@@ -34,4 +34,14 @@ class App extends \Kanboard\Core\Base
{ {
return $this->color->getList(); return $this->color->getList();
} }
public function getApplicationRoles()
{
return $this->role->getApplicationRoles();
}
public function getProjectRoles()
{
return $this->role->getProjectRoles();
}
} }

View File

@@ -23,7 +23,7 @@ class Auth extends Base
*/ */
public function checkCredentials($username, $password, $class, $method) public function checkCredentials($username, $password, $class, $method)
{ {
$this->container['dispatcher']->dispatch('app.bootstrap'); $this->dispatcher->dispatch('app.bootstrap');
if ($this->isUserAuthenticated($username, $password)) { if ($this->isUserAuthenticated($username, $password)) {
$this->checkProcedurePermission(true, $method); $this->checkProcedurePermission(true, $method);

View File

@@ -32,14 +32,18 @@ class File extends \Kanboard\Core\Base
} }
} catch (ObjectStorageException $e) { } catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage()); $this->logger->error($e->getMessage());
return '';
} }
return '';
} }
public function createFile($project_id, $task_id, $filename, $blob) public function createFile($project_id, $task_id, $filename, $blob)
{ {
return $this->file->uploadContent($project_id, $task_id, $filename, $blob); try {
return $this->file->uploadContent($project_id, $task_id, $filename, $blob);
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
return false;
}
} }
public function removeFile($file_id) public function removeFile($file_id)

49
app/Api/Group.php Normal file
View File

@@ -0,0 +1,49 @@
<?php
namespace Kanboard\Api;
/**
* Group API controller
*
* @package api
* @author Frederic Guillot
*/
class Group extends \Kanboard\Core\Base
{
public function createGroup($name, $external_id = '')
{
return $this->group->create($name, $external_id);
}
public function updateGroup($group_id, $name = null, $external_id = null)
{
$values = array(
'id' => $group_id,
'name' => $name,
'external_id' => $external_id,
);
foreach ($values as $key => $value) {
if (is_null($value)) {
unset($values[$key]);
}
}
return $this->group->update($values);
}
public function removeGroup($group_id)
{
return $this->group->remove($group_id);
}
public function getGroup($group_id)
{
return $this->group->getById($group_id);
}
public function getAllGroups()
{
return $this->group->getAll();
}
}

32
app/Api/GroupMember.php Normal file
View File

@@ -0,0 +1,32 @@
<?php
namespace Kanboard\Api;
/**
* Group Member API controller
*
* @package api
* @author Frederic Guillot
*/
class GroupMember extends \Kanboard\Core\Base
{
public function getGroupMembers($group_id)
{
return $this->groupMember->getMembers($group_id);
}
public function addGroupMember($group_id, $user_id)
{
return $this->groupMember->addUser($group_id, $user_id);
}
public function removeGroupMember($group_id, $user_id)
{
return $this->groupMember->removeUser($group_id, $user_id);
}
public function isGroupMember($group_id, $user_id)
{
return $this->groupMember->isMember($group_id, $user_id);
}
}

View File

@@ -5,25 +5,68 @@ namespace Kanboard\Api;
use Kanboard\Core\Security\Role; use Kanboard\Core\Security\Role;
/** /**
* ProjectPermission API controller * Project Permission API controller
* *
* @package api * @package api
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class ProjectPermission extends \Kanboard\Core\Base class ProjectPermission extends \Kanboard\Core\Base
{ {
public function getMembers($project_id) public function getProjectUsers($project_id)
{ {
return $this->projectUserRole->getAllUsers($project_id); return $this->projectUserRole->getAllUsers($project_id);
} }
public function revokeUser($project_id, $user_id) public function getAssignableUsers($project_id, $prepend_unassigned = false)
{
return $this->projectUserRole->getAssignableUsersList($project_id, $prepend_unassigned);
}
public function addProjectUser($project_id, $user_id, $role = Role::PROJECT_MEMBER)
{
return $this->projectUserRole->addUser($project_id, $user_id, $role);
}
public function addProjectGroup($project_id, $group_id, $role = Role::PROJECT_MEMBER)
{
return $this->projectGroupRole->addGroup($project_id, $group_id, $role);
}
public function removeProjectUser($project_id, $user_id)
{ {
return $this->projectUserRole->removeUser($project_id, $user_id); return $this->projectUserRole->removeUser($project_id, $user_id);
} }
public function removeProjectGroup($project_id, $group_id)
{
return $this->projectGroupRole->removeGroup($project_id, $group_id);
}
public function changeProjectUserRole($project_id, $user_id, $role)
{
return $this->projectUserRole->changeUserRole($project_id, $user_id, $role);
}
public function changeProjectGroupRole($project_id, $group_id, $role)
{
return $this->projectGroupRole->changeGroupRole($project_id, $group_id, $role);
}
// Deprecated
public function getMembers($project_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) public function allowUser($project_id, $user_id)
{ {
return $this->projectUserRole->addUser($project_id, $user_id, Role::PROJECT_MEMBER); return $this->addProjectUser($project_id, $user_id);
} }
} }

View File

@@ -50,7 +50,7 @@ class Role
} }
/** /**
* Get application roles * Get role name
* *
* @access public * @access public
* @param string $role * @param string $role

View File

@@ -229,3 +229,63 @@ Response example:
} }
} }
``` ```
### getApplicationRoles
- Purpose: **Get the application roles**
- Parameters: none
- Result: **Dictionary of role => role_name**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "getApplicationRoles",
"id": 317154243
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 317154243,
"result": {
"app-admin": "Administrator",
"app-manager": "Manager",
"app-user": "User"
}
}
```
### getProjectRoles
- Purpose: **Get the project roles**
- Parameters: none
- Result: **Dictionary of role => role_name**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "getProjectRoles",
"id": 8981960
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 8981960,
"result": {
"project-manager": "Project Manager",
"project-member": "Project Member",
"project-viewer": "Project Viewer"
}
}
```

View File

@@ -0,0 +1,152 @@
Group Member API Procedures
===========================
### getGroupMembers
- Purpose: **Get all members of a group**
- Parameters:
- **group_id** (integer, required)
- Result on success: **List of users**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "getGroupMembers",
"id": 1987176726,
"params": [
"1"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 1987176726,
"result": [
{
"group_id": "1",
"user_id": "1",
"id": "1",
"username": "admin",
"is_ldap_user": "0",
"name": null,
"email": null,
"notifications_enabled": "0",
"timezone": null,
"language": null,
"disable_login_form": "0",
"notifications_filter": "4",
"nb_failed_login": "0",
"lock_expiration_date": "0",
"is_project_admin": "0",
"gitlab_id": null,
"role": "app-admin"
}
]
}
```
### addGroupMember
- Purpose: **Add a user to a group**
- Parameters:
- **group_id** (integer, required)
- **user_id** (integer, required)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "addGroupMember",
"id": 1589058273,
"params": [
1,
1
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 1589058273,
"result": true
}
```
### removeGroupMember
- Purpose: **Remove a user from a group**
- Parameters:
- **group_id** (integer, required)
- **user_id** (integer, required)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "removeGroupMember",
"id": 1730416406,
"params": [
1,
1
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 1730416406,
"result": true
}
```
### isGroupMember
- Purpose: **Check if a user is member of a group**
- Parameters:
- **group_id** (integer, required)
- **user_id** (integer, required)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "isGroupMember",
"id": 1052800865,
"params": [
1,
1
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 1052800865,
"result": false
}
```

View File

@@ -0,0 +1,174 @@
Group API Procedures
====================
### createGroup
- Purpose: **Create a new group**
- Parameters:
- **name** (string, required)
- **external_id** (string, optional)
- Result on success: **link_id**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "createGroup",
"id": 1416806551,
"params": [
"My Group B",
"1234"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 1416806551,
"result": 2
}
```
### updateGroup
- Purpose: **Update a group**
- Parameters:
- **group_id** (integer, required)
- **name** (string, optional)
- **external_id** (string, optional)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "updateGroup",
"id": 866078030,
"params": {
"group_id": "1",
"name": "ABC",
"external_id": "something"
}
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 866078030,
"result": true
}
```
### removeGroup
- Purpose: **Remove a group**
- Parameters:
- **group_id** (integer, required)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "removeGroup",
"id": 566000661,
"params": [
"1"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 566000661,
"result": true
}
```
### getGroup
- Purpose: **Get one group**
- Parameters:
- **group_id** (integer, required)
- Result on success: **Group dictionary**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "getGroup",
"id": 1968647622,
"params": [
"1"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 1968647622,
"result": {
"id": "1",
"external_id": "",
"name": "My Group A"
}
}
```
### getAllGroups
- Purpose: **Get all groups**
- Parameters: none
- Result on success: **list of groups**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "getAllGroups",
"id": 546070742
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 546070742,
"result": [
{
"id": "1",
"external_id": "",
"name": "My Group A"
},
{
"id": "2",
"external_id": "1234",
"name": "My Group B"
}
]
}
```

View File

@@ -48,16 +48,19 @@ Usage
- [Authentication](api-authentication.markdown) - [Authentication](api-authentication.markdown)
- [Examples](api-examples.markdown) - [Examples](api-examples.markdown)
- [Application Procedures](api-application-procedures.markdown) - [Application](api-application-procedures.markdown)
- [Project Procedures](api-project-procedures.markdown) - [Projects](api-project-procedures.markdown)
- [Board Procedures](api-board-procedures.markdown) - [Project Permissions](api-project-permission-procedures.markdown)
- [Swimlane Procedures](api-swimlane-procedures.markdown) - [Boards](api-board-procedures.markdown)
- [Category Procedures](api-category-procedures.markdown) - [Swimlanes](api-swimlane-procedures.markdown)
- [Automatic Action Procedures](api-action-procedures.markdown) - [Categories](api-category-procedures.markdown)
- [Task Procedures](api-task-procedures.markdown) - [Automatic Actions](api-action-procedures.markdown)
- [Subtask Procedures](api-subtask-procedures.markdown) - [Tasks](api-task-procedures.markdown)
- [File Procedures](api-file-procedures.markdown) - [Subtasks](api-subtask-procedures.markdown)
- [Link Procedures](api-link-procedures.markdown) - [Files](api-file-procedures.markdown)
- [Comment Procedures](api-comment-procedures.markdown) - [Links](api-link-procedures.markdown)
- [User Procedures](api-user-procedures.markdown) - [Comments](api-comment-procedures.markdown)
- [User API Access Procedures](api-me-procedures.markdown) - [Users](api-user-procedures.markdown)
- [Groups](api-group-procedures.markdown)
- [Group Members](api-group-member-procedures.markdown)
- [Me](api-me-procedures.markdown)

View File

@@ -0,0 +1,274 @@
Project Permission API Procedures
=================================
### getProjectUsers
- Purpose: **Get all members of a project**
- Parameters:
- **project_id** (integer, required)
- Result on success: **Dictionary of user_id => user name**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "getProjectUsers",
"id": 1601016721,
"params": [
"1"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 1601016721,
"result": {
"1": "admin"
}
}
```
### getAssignableUsers
- Purpose: **Get users that can be assigned to a task for a project** (all members except viewers)
- Parameters:
- **project_id** (integer, required)
- **prepend_unassigned** (boolean, optional, default is false)
- Result on success: **Dictionary of user_id => user name**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "getAssignableUsers",
"id": 658294870,
"params": [
"1"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 658294870,
"result": {
"1": "admin"
}
}
```
### addProjectUser
- Purpose: **Grant access to a project for a user**
- Parameters:
- **project_id** (integer, required)
- **user_id** (integer, required)
- **role** (string, optional)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "addProjectUser",
"id": 1294688355,
"params": [
"1",
"1",
"project-viewer"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 1294688355,
"result": true
}
```
### addProjectGroup
- Purpose: **Grant access to a project for a group**
- Parameters:
- **project_id** (integer, required)
- **group_id** (integer, required)
- **role** (string, optional)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "addProjectGroup",
"id": 1694959089,
"params": [
"1",
"1"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 1694959089,
"result": true
}
```
### removeProjectUser
- Purpose: **Revoke user access to a project**
- Parameters:
- **project_id** (integer, required)
- **user_id** (integer, required)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "removeProjectUser",
"id": 645233805,
"params": [
1,
1
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 645233805,
"result": true
}
```
### removeProjectGroup
- Purpose: **Revoke group access to a project**
- Parameters:
- **project_id** (integer, required)
- **group_id** (integer, required)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "removeProjectGroup",
"id": 557146966,
"params": [
1,
1
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 557146966,
"result": true
}
```
### changeProjectUserRole
- Purpose: **Change role of a user for a project**
- Parameters:
- **project_id** (integer, required)
- **user_id** (integer, required)
- **role** (string, required)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "changeProjectUserRole",
"id": 193473170,
"params": [
"1",
"1",
"project-viewer"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 193473170,
"result": true
}
```
### changeProjectGroupRole
- Purpose: **Change role of a group for a project**
- Parameters:
- **project_id** (integer, required)
- **group_id** (integer, required)
- **role** (string, required)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "changeProjectGroupRole",
"id": 2114673298,
"params": [
"1",
"1",
"project-viewer"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 2114673298,
"result": true
}
```

View File

@@ -409,104 +409,3 @@ Request example:
] ]
} }
``` ```
### getMembers
- Purpose: **Get members of a project**
- Parameters:
- **project_id** (integer, required)
- Result on success: Key/value pair of user_id and username
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "getMembers",
"id": 1944388643,
"params": [
1
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 1944388643,
"result": {
"1": "user1",
"2": "user2",
"3": "user3"
}
}
```
### revokeUser
- Purpose: **Revoke user access for a given project**
- Parameters:
- **project_id** (integer, required)
- **user_id** (integer, required)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "revokeUser",
"id": 251218350,
"params": [
1,
2
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 251218350,
"result": true
}
```
### allowUser
- Purpose: **Grant user access for a given project**
- Parameters:
- **project_id** (integer, required)
- **user_id** (integer, required)
- Result on success: **true**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "allowUser",
"id": 2111451404,
"params": [
1,
2
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 2111451404,
"result": true
}
```

View File

@@ -1,7 +1,7 @@
How to run units and functional tests? Automated tests
====================================== ===============
[PHPUnit](https://phpunit.de/) is used to run automatic tests on Kanboard. [PHPUnit](https://phpunit.de/) is used to run automated tests on Kanboard.
You can run tests across different databases (Sqlite, Mysql and Postgresql) to be sure that the result is the same everywhere. You can run tests across different databases (Sqlite, Mysql and Postgresql) to be sure that the result is the same everywhere.
@@ -9,31 +9,18 @@ Requirements
------------ ------------
- Linux/Unix machine - Linux/Unix machine
- PHP command line - PHP cli
- PHPUnit installed - PHPUnit installed
- Mysql and Postgresql (optional) - Mysql and Postgresql (optional)
Install the latest version of PHPUnit Unit Tests
------------------------------------- ----------
Simply download the PHPUnit PHAR et copy the file somewhere in your `$PATH`: ### Test with Sqlite
```bash
wget https://phar.phpunit.de/phpunit.phar
chmod +x phpunit.phar
sudo mv phpunit.phar /usr/local/bin/phpunit
phpunit --version
PHPUnit 4.2.6 by Sebastian Bergmann.
```
Running unit tests
------------------
### Testing with Sqlite
Sqlite tests use a in-memory database, nothing is written on the file system. Sqlite tests use a in-memory database, nothing is written on the file system.
The config file is `tests/units.sqlite.xml`. The PHPUnit config file is `tests/units.sqlite.xml`.
From your Kanboard directory, run the command `phpunit -c tests/units.sqlite.xml`. From your Kanboard directory, run the command `phpunit -c tests/units.sqlite.xml`.
Example: Example:
@@ -41,21 +28,26 @@ Example:
```bash ```bash
phpunit -c tests/units.sqlite.xml phpunit -c tests/units.sqlite.xml
PHPUnit 4.2.6 by Sebastian Bergmann. PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
Configuration read from /Volumes/Devel/apps/kanboard/tests/units.sqlite.xml ............................................................... 63 / 649 ( 9%)
............................................................... 126 / 649 ( 19%)
............................................................... 189 / 649 ( 29%)
............................................................... 252 / 649 ( 38%)
............................................................... 315 / 649 ( 48%)
............................................................... 378 / 649 ( 58%)
............................................................... 441 / 649 ( 67%)
............................................................... 504 / 649 ( 77%)
............................................................... 567 / 649 ( 87%)
............................................................... 630 / 649 ( 97%)
................... 649 / 649 (100%)
................................................................. 65 / 74 ( 87%) Time: 1.22 minutes, Memory: 151.25Mb
.........
Time: 9.05 seconds, Memory: 17.75Mb OK (649 tests, 43595 assertions)
OK (74 tests, 6145 assertions)
``` ```
**NOTE:** PHPUnit is already included in the Vagrant environment ### Test with Mysql
### Testing with Mysql
You must have Mysql or MariaDb installed on localhost. You must have Mysql or MariaDb installed on localhost.
@@ -68,27 +60,10 @@ By default, those credentials are used:
For each execution the database is dropped and created again. For each execution the database is dropped and created again.
The config file is `tests/units.mysql.xml`. The PHPUnit config file is `tests/units.mysql.xml`.
From your Kanboard directory, run the command `phpunit -c tests/units.mysql.xml`. From your Kanboard directory, run the command `phpunit -c tests/units.mysql.xml`.
Example: ### Test with Postgresql
```bash
phpunit -c tests/units.mysql.xml
PHPUnit 4.2.6 by Sebastian Bergmann.
Configuration read from /Volumes/Devel/apps/kanboard/tests/units.mysql.xml
................................................................. 65 / 74 ( 87%)
.........
Time: 49.77 seconds, Memory: 17.50Mb
OK (74 tests, 6145 assertions)
```
### Testing with Postgresql
You must have Postgresql installed on localhost. You must have Postgresql installed on localhost.
@@ -100,37 +75,20 @@ By default, those credentials are used:
- Database: **kanboard_unit_test** - Database: **kanboard_unit_test**
Be sure to allow the user `postgres` to create and drop databases. Be sure to allow the user `postgres` to create and drop databases.
For each execution the database is dropped and created again. The database is recreated for each execution.
The config file is `tests/units.postgres.xml`. The PHPUnit config file is `tests/units.postgres.xml`.
From your Kanboard directory, run the command `phpunit -c tests/units.postgres.xml`. From your Kanboard directory, run the command `phpunit -c tests/units.postgres.xml`.
Example: Integration Tests
-----------------
```bash
phpunit -c tests/units.postgres.xml
PHPUnit 4.2.6 by Sebastian Bergmann.
Configuration read from /Volumes/Devel/apps/kanboard/tests/units.postgres.xml
................................................................. 65 / 74 ( 87%)
.........
Time: 52.66 seconds, Memory: 17.50Mb
OK (74 tests, 6145 assertions)
```
Running functionals tests
-------------------------
Actually only the API calls are tested. Actually only the API calls are tested.
Real HTTP calls are made with those tests. Real HTTP calls are made with those tests.
So a local instance of Kanboard is necessary and must listen on `http://localhost:8000`. So a local instance of Kanboard is necessary and must listen on `http://localhost:8000/`.
Don't forget that all data will be removed/altered by the test suite. All data will be removed/altered by the test suite.
Moreover the script will reset and set a new API key. Moreover the script will reset and set a new API key.
1. Start a local instance of Kanboard `php -S 127.0.0.1:8000` 1. Start a local instance of Kanboard `php -S 127.0.0.1:8000`
@@ -138,27 +96,27 @@ Moreover the script will reset and set a new API key.
The same method as above is used to run tests across different databases: The same method as above is used to run tests across different databases:
- Sqlite: `phpunit -c tests/functionals.sqlite.xml` - Sqlite: `phpunit -c tests/integration.sqlite.xml`
- Mysql: `phpunit -c tests/functionals.mysql.xml` - Mysql: `phpunit -c tests/integration.mysql.xml`
- Postgresql: `phpunit -c tests/functionals.postgres.xml` - Postgresql: `phpunit -c tests/integration.postgres.xml`
Example: Example:
```bash ```bash
phpunit -c tests/functionals.sqlite.xml phpunit -c tests/integration.sqlite.xml
PHPUnit 4.2.6 by Sebastian Bergmann. PHPUnit 5.0.0 by Sebastian Bergmann and contributors.
Configuration read from /Volumes/Devel/apps/kanboard/tests/functionals.sqlite.xml ............................................................... 63 / 135 ( 46%)
............................................................... 126 / 135 ( 93%)
......... 135 / 135 (100%)
.......................................... Time: 1.18 minutes, Memory: 14.75Mb
Time: 1.72 seconds, Memory: 4.25Mb OK (135 tests, 526 assertions)
OK (42 tests, 160 assertions)
``` ```
Continuous Integration with Travis-ci Continuous Integration with Travis-CI
------------------------------------- -------------------------------------
After each commit pushed on the main repository, unit tests are executed across 5 different versions of PHP: After each commit pushed on the main repository, unit tests are executed across 5 different versions of PHP:
@@ -171,6 +129,4 @@ After each commit pushed on the main repository, unit tests are executed across
Each version of PHP is tested against the 3 supported database: Sqlite, Mysql and Postgresql. Each version of PHP is tested against the 3 supported database: Sqlite, Mysql and Postgresql.
That mean we run 15 jobs each time the repository is updated. The execution time is around 25 minutes.
The Travis config file `.travis.yml` is located on the root directory of Kanboard. The Travis config file `.travis.yml` is located on the root directory of Kanboard.

View File

@@ -19,6 +19,8 @@ use Kanboard\Api\Swimlane;
use Kanboard\Api\Task; use Kanboard\Api\Task;
use Kanboard\Api\TaskLink; use Kanboard\Api\TaskLink;
use Kanboard\Api\User; use Kanboard\Api\User;
use Kanboard\Api\Group;
use Kanboard\Api\GroupMember;
$server = new Server; $server = new Server;
$server->setAuthenticationHeader(API_AUTHENTICATION_HEADER); $server->setAuthenticationHeader(API_AUTHENTICATION_HEADER);
@@ -39,5 +41,7 @@ $server->attach(new Swimlane($container));
$server->attach(new Task($container)); $server->attach(new Task($container));
$server->attach(new TaskLink($container)); $server->attach(new TaskLink($container));
$server->attach(new User($container)); $server->attach(new User($container));
$server->attach(new Group($container));
$server->attach(new GroupMember($container));
echo $server->execute(); echo $server->execute();

View File

@@ -1,7 +1,7 @@
<phpunit stopOnError="true" stopOnFailure="true" colors="true"> <phpunit stopOnError="true" stopOnFailure="true" colors="true">
<testsuites> <testsuites>
<testsuite name="Kanboard"> <testsuite name="Kanboard">
<directory>functionals</directory> <directory>integration</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<php> <php>

View File

@@ -1,7 +1,7 @@
<phpunit stopOnError="true" stopOnFailure="true" colors="true"> <phpunit stopOnError="true" stopOnFailure="true" colors="true">
<testsuites> <testsuites>
<testsuite name="Kanboard"> <testsuite name="Kanboard">
<directory>functionals</directory> <directory>integration</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<php> <php>

View File

@@ -1,7 +1,7 @@
<phpunit stopOnError="true" stopOnFailure="true" colors="true"> <phpunit stopOnError="true" stopOnFailure="true" colors="true">
<testsuites> <testsuites>
<testsuite name="Kanboard"> <testsuite name="Kanboard">
<directory>functionals</directory> <directory>integration</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<php> <php>

View File

@@ -45,16 +45,6 @@ class Api extends PHPUnit_Framework_TestCase
return $tasks[0]['id']; return $tasks[0]['id'];
} }
public function testGetTimezone()
{
$this->assertEquals('Europe/Paris', $this->client->getTimezone());
}
public function testGetVersion()
{
$this->assertEquals('master', $this->client->getVersion());
}
public function testRemoveAll() public function testRemoveAll()
{ {
$projects = $this->client->getAllProjects(); $projects = $this->client->getAllProjects();

View File

@@ -0,0 +1,34 @@
<?php
require_once __DIR__.'/Base.php';
class AppTest extends Base
{
public function testGetTimezone()
{
$this->assertEquals('UTC', $this->app->getTimezone());
}
public function testGetVersion()
{
$this->assertEquals('master', $this->app->getVersion());
}
public function testGetApplicationRoles()
{
$roles = $this->app->getApplicationRoles();
$this->assertCount(3, $roles);
$this->assertEquals('Administrator', $roles['app-admin']);
$this->assertEquals('Manager', $roles['app-manager']);
$this->assertEquals('User', $roles['app-user']);
}
public function testGetProjectRoles()
{
$roles = $this->app->getProjectRoles();
$this->assertCount(3, $roles);
$this->assertEquals('Project Manager', $roles['project-manager']);
$this->assertEquals('Project Member', $roles['project-member']);
$this->assertEquals('Project Viewer', $roles['project-viewer']);
}
}

View File

@@ -0,0 +1,62 @@
<?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->debug = true;
$this->admin = new JsonRPC\Client(API_URL);
$this->admin->authentication('admin', 'admin');
// $this->admin->debug = true;
$this->user = new JsonRPC\Client(API_URL);
$this->user->authentication('user', 'password');
// $this->user->debug = true;
}
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'];
}
}

View File

@@ -0,0 +1,39 @@
<?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 testRemove()
{
$groupId = $this->getGroupId();
$this->assertTrue($this->app->removeGroupMember($groupId, 1));
$this->assertFalse($this->app->isGroupMember($groupId, 1));
}
}

View File

@@ -0,0 +1,48 @@
<?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());
}
}

View File

@@ -1,51 +1,9 @@
<?php <?php
require_once __DIR__.'/../../vendor/autoload.php'; require_once __DIR__.'/Base.php';
class UserApi extends PHPUnit_Framework_TestCase class MeTest extends Base
{ {
private $app = null;
private $admin = null;
private $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->debug = true;
$this->admin = new JsonRPC\Client(API_URL);
$this->admin->authentication('admin', 'admin');
// $this->admin->debug = true;
$this->user = new JsonRPC\Client(API_URL);
$this->user->authentication('user', 'password');
// $this->user->debug = true;
}
public function testCreateProject() public function testCreateProject()
{ {
$this->assertEquals(1, $this->app->createProject('team project')); $this->assertEquals(1, $this->app->createProject('team project'));

View File

@@ -0,0 +1,64 @@
<?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));
}
}