Added basic API endpoints (#258)
This commit is contained in:
169
tests/API/APIControllerBaseTest.php
Normal file
169
tests/API/APIControllerBaseTest.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\DataFixtures\UserFixtures;
|
||||
use App\Entity\User;
|
||||
use App\Tests\Controller\ControllerBaseTest;
|
||||
use Symfony\Bundle\FrameworkBundle\Client;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Adds some useful functions for writing API integration tests.
|
||||
*/
|
||||
abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
{
|
||||
/**
|
||||
* @param string $role
|
||||
* @return Client
|
||||
*/
|
||||
protected function getClientForAuthenticatedUser(string $role = User::ROLE_USER)
|
||||
{
|
||||
switch ($role) {
|
||||
case User::ROLE_SUPER_ADMIN:
|
||||
$client = self::createClient([], [
|
||||
'HTTP_X_AUTH_USER' => UserFixtures::USERNAME_SUPER_ADMIN,
|
||||
'HTTP_X_AUTH_TOKEN' => UserFixtures::DEFAULT_API_TOKEN,
|
||||
]);
|
||||
break;
|
||||
|
||||
case User::ROLE_ADMIN:
|
||||
$client = self::createClient([], [
|
||||
'HTTP_X_AUTH_USER' => UserFixtures::USERNAME_ADMIN,
|
||||
'HTTP_X_AUTH_TOKEN' => UserFixtures::DEFAULT_API_TOKEN,
|
||||
]);
|
||||
break;
|
||||
|
||||
case User::ROLE_TEAMLEAD:
|
||||
$client = self::createClient([], [
|
||||
'HTTP_X_AUTH_USER' => UserFixtures::USERNAME_TEAMLEAD,
|
||||
'HTTP_X_AUTH_TOKEN' => UserFixtures::DEFAULT_API_TOKEN,
|
||||
]);
|
||||
break;
|
||||
|
||||
case User::ROLE_USER:
|
||||
$client = self::createClient([], [
|
||||
'HTTP_X_AUTH_USER' => UserFixtures::USERNAME_USER,
|
||||
'HTTP_X_AUTH_TOKEN' => UserFixtures::DEFAULT_API_TOKEN,
|
||||
]);
|
||||
break;
|
||||
|
||||
default:
|
||||
$client = null;
|
||||
break;
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param bool $json
|
||||
* @return string
|
||||
*/
|
||||
protected function createUrl($url, $json = true)
|
||||
{
|
||||
return '/' . ltrim($url, '/') . ($json ? '.json' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
*/
|
||||
protected function assertRequestIsSecured(Client $client, string $url, $method = 'GET')
|
||||
{
|
||||
$this->request($client, $url, $method);
|
||||
$this->assertResponseIsSecured($client->getResponse(), $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
* @param string $url
|
||||
*/
|
||||
protected function assertResponseIsSecured(Response $response, string $url)
|
||||
{
|
||||
$data = ['message' => 'Authentication required, missing headers: X-AUTH-USER, X-AUTH-TOKEN'];
|
||||
|
||||
$this->assertEquals(
|
||||
$data,
|
||||
json_decode($response->getContent(), true),
|
||||
sprintf('The secure URL %s is not protected.', $url)
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Response::HTTP_FORBIDDEN, // TODO that should actually be Response::HTTP_UNAUTHORIZED
|
||||
$response->getStatusCode(),
|
||||
sprintf('The secure URL %s has the wrong status code %s.', $url, $response->getStatusCode())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
*/
|
||||
protected function assertUrlIsSecuredForRole(string $role, string $url, string $method = 'GET')
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$client->request($method, $this->createUrl($url));
|
||||
|
||||
$this->assertFalse(
|
||||
$client->getResponse()->isSuccessful(),
|
||||
sprintf('The secure URL %s is not protected for role %s', $url, $role)
|
||||
);
|
||||
|
||||
$expected = [
|
||||
'code' => 403,
|
||||
'message' => 'Access denied.'
|
||||
];
|
||||
|
||||
$this->assertEquals(403, $client->getResponse()->getStatusCode());
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
json_decode($client->getResponse()->getContent(), true)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
* @return Crawler
|
||||
*/
|
||||
protected function request(Client $client, string $url, $method = 'GET')
|
||||
{
|
||||
return $client->request($method, $this->createUrl($url), [], [], ['HTTP_CONTENT_TYPE' => 'application/json']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @param string $url
|
||||
*/
|
||||
protected function assertEntityNotFound(string $role, string $url)
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$this->request($client, $url);
|
||||
|
||||
$expected = [
|
||||
'code' => 404,
|
||||
'message' => 'Not found'
|
||||
];
|
||||
|
||||
$this->assertEquals(404, $client->getResponse()->getStatusCode());
|
||||
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
json_decode($client->getResponse()->getContent(), true)
|
||||
);
|
||||
}
|
||||
}
|
||||
64
tests/API/ActivityControllerTest.php
Normal file
64
tests/API/ActivityControllerTest.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\Entity\User;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \App\API\ActivityController
|
||||
* @group integration
|
||||
* @group legacy
|
||||
*/
|
||||
class ActivityControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/activities');
|
||||
}
|
||||
|
||||
public function testGetCollection()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/activities');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertEquals(1, count($result));
|
||||
$this->assertStructure($result[0]);
|
||||
}
|
||||
|
||||
public function testGetEntity()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/activities/1');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertStructure($result);
|
||||
}
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/activities/2');
|
||||
}
|
||||
|
||||
protected function assertStructure(array $result)
|
||||
{
|
||||
$expectedKeys = [
|
||||
'id', 'name', 'comment', 'visible', 'project_id'
|
||||
];
|
||||
|
||||
$actual = array_keys($result);
|
||||
|
||||
$this->assertEquals(count($expectedKeys), count($actual), 'Activity entity has different amount of keys');
|
||||
$this->assertEquals($expectedKeys, $actual, 'Activity structure does not match');
|
||||
}
|
||||
}
|
||||
51
tests/API/ApiDocControllerTest.php
Normal file
51
tests/API/ApiDocControllerTest.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Tests\Controller\ControllerBaseTest;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
* @group legacy
|
||||
*/
|
||||
class ApiDocControllerTest extends ControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/doc');
|
||||
}
|
||||
|
||||
public function testGetDocs()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/doc');
|
||||
$this->assertContains('<title>Kimai 2 - API Docs</title>', $client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
public function testGetJsonDocs()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/doc.json');
|
||||
$this->assertContains('"title":"Kimai 2 - API Docs"', $client->getResponse()->getContent());
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertNotEmpty($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
protected function createUrl($url)
|
||||
{
|
||||
return '/' . ltrim($url, '/');
|
||||
}
|
||||
}
|
||||
65
tests/API/CustomerControllerTest.php
Normal file
65
tests/API/CustomerControllerTest.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\Entity\User;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \App\API\CustomerController
|
||||
* @group integration
|
||||
* @group legacy
|
||||
*/
|
||||
class CustomerControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/customers');
|
||||
}
|
||||
|
||||
public function testGetCollection()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/customers');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertEquals(1, count($result));
|
||||
$this->assertStructure($result[0]);
|
||||
}
|
||||
|
||||
public function testGetEntity()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/customers/1');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertStructure($result);
|
||||
}
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/customers/2');
|
||||
}
|
||||
|
||||
protected function assertStructure(array $result)
|
||||
{
|
||||
$expectedKeys = [
|
||||
'id', 'name', 'number', 'comment', 'visible', 'company', 'contact', 'address', 'country', 'currency',
|
||||
'phone', 'fax', 'mobile', 'mail', 'timezone'
|
||||
];
|
||||
|
||||
$actual = array_keys($result);
|
||||
|
||||
$this->assertEquals(count($expectedKeys), count($actual), 'Customer entity has different amount of keys');
|
||||
$this->assertEquals($expectedKeys, $actual, 'Customer structure does not match');
|
||||
}
|
||||
}
|
||||
35
tests/API/HealthcheckControllerTest.php
Normal file
35
tests/API/HealthcheckControllerTest.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\Entity\User;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \App\API\HealthcheckController
|
||||
* @group integration
|
||||
* @group legacy
|
||||
*/
|
||||
class HealthcheckControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/ping');
|
||||
}
|
||||
|
||||
public function testPing()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/ping');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertEquals(['message' => 'pong'], $result);
|
||||
}
|
||||
}
|
||||
64
tests/API/ProjectControllerTest.php
Normal file
64
tests/API/ProjectControllerTest.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\Entity\User;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \App\API\ProjectController
|
||||
* @group integration
|
||||
* @group legacy
|
||||
*/
|
||||
class ProjectControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/projects');
|
||||
}
|
||||
|
||||
public function testGetCollection()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/projects');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertEquals(1, count($result));
|
||||
$this->assertStructure($result[0]);
|
||||
}
|
||||
|
||||
public function testGetEntity()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/projects/1');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertStructure($result);
|
||||
}
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/projects/2');
|
||||
}
|
||||
|
||||
protected function assertStructure(array $result)
|
||||
{
|
||||
$expectedKeys = [
|
||||
'id', 'name', 'comment', 'visible', 'budget', 'order_number', 'customer_id'
|
||||
];
|
||||
|
||||
$actual = array_keys($result);
|
||||
|
||||
$this->assertEquals(count($expectedKeys), count($actual), 'Project entity has different amount of keys');
|
||||
$this->assertEquals($expectedKeys, $actual, 'Project structure does not match');
|
||||
}
|
||||
}
|
||||
65
tests/API/UserControllerTest.php
Normal file
65
tests/API/UserControllerTest.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\Entity\User;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \App\API\UserController
|
||||
* @group integration
|
||||
* @group legacy
|
||||
*/
|
||||
class UserControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/users');
|
||||
$this->assertUrlIsSecuredForRole(User::ROLE_ADMIN, '/api/users');
|
||||
}
|
||||
|
||||
public function testGetCollection()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/api/users');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertEquals(6, count($result));
|
||||
$this->assertStructure($result[0]);
|
||||
}
|
||||
|
||||
public function testGetEntity()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
$this->assertAccessIsGranted($client, '/api/users/1');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertStructure($result);
|
||||
}
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_SUPER_ADMIN, '/api/users/99');
|
||||
}
|
||||
|
||||
protected function assertStructure(array $result)
|
||||
{
|
||||
$expectedKeys = [
|
||||
'id', 'username', 'enabled', 'roles', 'alias', 'title', 'avatar'
|
||||
];
|
||||
|
||||
$actual = array_keys($result);
|
||||
|
||||
$this->assertEquals(count($expectedKeys), count($actual), 'User entity has different amount of keys');
|
||||
$this->assertEquals($expectedKeys, $actual, 'User structure does not match');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user