Next major version 2 with PHP 8.1, Symfony 6, Tabler UI, 2FA ... (#2902)
This commit is contained in:
@@ -65,7 +65,20 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
return '/' . ltrim($url, '/');
|
||||
}
|
||||
|
||||
protected function assertRequestIsSecured(HttpKernelBrowser $client, string $url, $method = 'GET')
|
||||
protected function assertPagination(Response $response, int $page, int $pageSize, int $totalPages, int $totalResults): void
|
||||
{
|
||||
$this->assertTrue($response->headers->has('X-Page'), 'Missing "X-Page" header');
|
||||
$this->assertTrue($response->headers->has('X-Total-Count'), 'Missing "X-Total-Count" header');
|
||||
$this->assertTrue($response->headers->has('X-Total-Pages'), 'Missing "X-Total-Pages" header');
|
||||
$this->assertTrue($response->headers->has('X-Per-Page'), 'Missing "X-Per-Page" header');
|
||||
|
||||
$this->assertEquals($page, $response->headers->get('X-Page'));
|
||||
$this->assertEquals($totalResults, $response->headers->get('X-Total-Count'));
|
||||
$this->assertEquals($totalPages, $response->headers->get('X-Total-Pages'));
|
||||
$this->assertEquals($pageSize, $response->headers->get('X-Per-Page'));
|
||||
}
|
||||
|
||||
protected function assertRequestIsSecured(HttpKernelBrowser $client, string $url, $method = 'GET'): void
|
||||
{
|
||||
$this->request($client, $url, $method);
|
||||
$this->assertResponseIsSecured($client->getResponse(), $url);
|
||||
@@ -75,9 +88,9 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
* @param Response $response
|
||||
* @param string $url
|
||||
*/
|
||||
protected function assertResponseIsSecured(Response $response, string $url)
|
||||
protected function assertResponseIsSecured(Response $response, string $url): void
|
||||
{
|
||||
$data = ['message' => 'Authentication required, missing headers: X-AUTH-USER, X-AUTH-TOKEN'];
|
||||
$data = ['message' => 'Authentication required, missing user header: X-AUTH-USER'];
|
||||
|
||||
self::assertEquals(
|
||||
$data,
|
||||
@@ -97,7 +110,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
*/
|
||||
protected function assertUrlIsSecuredForRole(string $role, string $url, string $method = 'GET')
|
||||
protected function assertUrlIsSecuredForRole(string $role, string $url, string $method = 'GET'): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$client->request($method, $this->createUrl($url));
|
||||
@@ -108,107 +121,110 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
);
|
||||
|
||||
$this->assertApiException($client->getResponse(), [
|
||||
'code' => 403,
|
||||
'message' => 'Access denied.'
|
||||
'code' => Response::HTTP_FORBIDDEN,
|
||||
'message' => 'Forbidden'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function request(HttpKernelBrowser $client, string $url, $method = 'GET', array $parameters = [], string $content = null): Crawler
|
||||
public function request(HttpKernelBrowser $client, string $url, $method = 'GET', array $parameters = [], string $content = null): Crawler
|
||||
{
|
||||
$server = ['HTTP_CONTENT_TYPE' => 'application/json', 'CONTENT_TYPE' => 'application/json'];
|
||||
|
||||
return $client->request($method, $this->createUrl($url), $parameters, [], $server, $content);
|
||||
}
|
||||
|
||||
protected function assertEntityNotFound(string $role, string $url, string $method = 'GET', ?string $message = null)
|
||||
protected function assertEntityNotFound(string $role, string $url, string $method = 'GET', ?string $message = null): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$this->request($client, $url, $method);
|
||||
$this->assertApiException($client->getResponse(), [
|
||||
'code' => 404,
|
||||
'message' => $message ?? 'Not found'
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertNotFoundForDelete(HttpKernelBrowser $client, string $url)
|
||||
protected function assertNotFoundForDelete(HttpKernelBrowser $client, string $url): void
|
||||
{
|
||||
$this->assertExceptionForMethod($client, $url, 'DELETE', [], [
|
||||
'code' => 404,
|
||||
'message' => 'Not found'
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertEntityNotFoundForDelete(string $role, string $url, ?string $message = null)
|
||||
{
|
||||
$this->assertExceptionForDeleteAction($role, $url, [], [
|
||||
'code' => 404,
|
||||
'message' => $message ?? 'Not found'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertEntityNotFoundForPatch(string $role, string $url, array $data, ?string $message = null)
|
||||
protected function assertEntityNotFoundForPatch(string $role, string $url, array $data): void
|
||||
{
|
||||
$this->assertExceptionForPatchAction($role, $url, $data, [
|
||||
'code' => 404,
|
||||
'message' => $message ?? 'Not found',
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertEntityNotFoundForPost(string $role, string $url, array $data, ?string $message = null)
|
||||
protected function assertEntityNotFoundForPost(HttpKernelBrowser $client, string $url, array $data = []): void
|
||||
{
|
||||
$this->assertExceptionForPostAction($role, $url, $data, [
|
||||
'code' => 404,
|
||||
'message' => $message ?? 'Not found',
|
||||
$this->assertExceptionForMethod($client, $url, 'POST', $data, [
|
||||
'code' => Response::HTTP_NOT_FOUND,
|
||||
'message' => 'Not Found',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertExceptionForDeleteAction(string $role, string $url, array $data, array $expectedErrors)
|
||||
protected function assertExceptionForDeleteAction(string $role, string $url, array $data, array $expectedErrors): void
|
||||
{
|
||||
$this->assertExceptionForRole($role, $url, 'DELETE', $data, $expectedErrors);
|
||||
}
|
||||
|
||||
protected function assertExceptionForPatchAction(string $role, string $url, array $data, array $expectedErrors)
|
||||
protected function assertExceptionForPatchAction(string $role, string $url, array $data, array $expectedErrors): void
|
||||
{
|
||||
$this->assertExceptionForRole($role, $url, 'PATCH', $data, $expectedErrors);
|
||||
}
|
||||
|
||||
protected function assertExceptionForPostAction(string $role, string $url, array $data, array $expectedErrors)
|
||||
protected function assertExceptionForPostAction(string $role, string $url, array $data, array $expectedErrors): void
|
||||
{
|
||||
$this->assertExceptionForRole($role, $url, 'POST', $data, $expectedErrors);
|
||||
}
|
||||
|
||||
protected function assertExceptionForMethod(HttpKernelBrowser $client, string $url, string $method, array $data, array $expectedErrors)
|
||||
protected function assertExceptionForMethod(HttpKernelBrowser $client, string $url, string $method, array $data, array $expectedErrors): void
|
||||
{
|
||||
$this->request($client, $url, $method, [], json_encode($data));
|
||||
$this->assertApiException($client->getResponse(), $expectedErrors);
|
||||
}
|
||||
|
||||
protected function assertApiException(Response $response, array $expectedErrors)
|
||||
protected function assertApiException(Response $response, array $expectedErrors): void
|
||||
{
|
||||
self::assertFalse($response->isSuccessful());
|
||||
self::assertEquals($expectedErrors['code'], $response->getStatusCode());
|
||||
self::assertEquals($expectedErrors, json_decode($response->getContent(), true));
|
||||
}
|
||||
|
||||
protected function assertExceptionForRole(string $role, string $url, string $method, array $data, array $expectedErrors)
|
||||
protected function assertExceptionForRole(string $role, string $url, string $method, array $data, array $expectedErrors): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$this->assertExceptionForMethod($client, $url, $method, $data, $expectedErrors);
|
||||
}
|
||||
|
||||
protected function assertApi500Exception(Response $response, string $message)
|
||||
protected function assertApi500Exception(Response $response, string $message): void
|
||||
{
|
||||
$this->assertApiException($response, ['code' => 500, 'message' => $message]);
|
||||
$this->assertApiException($response, ['code' => Response::HTTP_INTERNAL_SERVER_ERROR, 'message' => $message]);
|
||||
}
|
||||
|
||||
protected function assertApiAccessDenied(HttpKernelBrowser $client, string $url, string $message)
|
||||
protected function assertBadRequest(HttpKernelBrowser $client, string $url, string $method): void
|
||||
{
|
||||
$this->assertExceptionForMethod($client, $url, $method, [], [
|
||||
'code' => Response::HTTP_BAD_REQUEST,
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function assertApiAccessDenied(HttpKernelBrowser $client, string $url, string $message = 'Forbidden'): void
|
||||
{
|
||||
$this->request($client, $url);
|
||||
$this->assertApiResponseAccessDenied($client->getResponse(), $message);
|
||||
}
|
||||
|
||||
protected function assertApiResponseAccessDenied(Response $response, string $message)
|
||||
protected function assertApiResponseAccessDenied(Response $response, string $message = 'Forbidden'): void
|
||||
{
|
||||
// APP_DEBUG = 1 means "real exception messages" - it is always overwritten
|
||||
$message = 'Forbidden';
|
||||
|
||||
$this->assertApiException($response, [
|
||||
'code' => Response::HTTP_FORBIDDEN,
|
||||
'message' => $message
|
||||
@@ -219,17 +235,24 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
* @param Response $response
|
||||
* @param array<int, string>|array<string, mixed> $failedFields
|
||||
* @param bool $extraFields test for the error "This form should not contain extra fields"
|
||||
* @param array<int, string>|array<string, mixed> $globalError
|
||||
*/
|
||||
protected function assertApiCallValidationError(Response $response, array $failedFields, bool $extraFields = false)
|
||||
protected function assertApiCallValidationError(Response $response, array $failedFields, bool $extraFields = false, array $globalError = []): void
|
||||
{
|
||||
self::assertFalse($response->isSuccessful());
|
||||
$result = json_decode($response->getContent(), true);
|
||||
|
||||
self::assertArrayHasKey('errors', $result);
|
||||
|
||||
if ($extraFields) {
|
||||
self::assertArrayHasKey('errors', $result['errors']);
|
||||
self::assertEquals($result['errors']['errors'][0], 'This form should not contain extra fields.');
|
||||
self::assertEquals('This form should not contain extra fields.', $result['errors']['errors'][0]);
|
||||
}
|
||||
|
||||
if (\count($globalError) > 0) {
|
||||
self::assertArrayHasKey('errors', $result['errors']);
|
||||
foreach ($globalError as $err) {
|
||||
self::assertTrue(\in_array($err, $result['errors']['errors']), 'Missing global validation error: ' . $err);
|
||||
}
|
||||
}
|
||||
|
||||
self::assertArrayHasKey('children', $result['errors']);
|
||||
@@ -275,6 +298,16 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
protected static function getExpectedResponseStructure(string $type): array
|
||||
{
|
||||
switch ($type) {
|
||||
case 'PageActionItem':
|
||||
return [
|
||||
'id' => 'string',
|
||||
'title' => '@string',
|
||||
'url' => '@string',
|
||||
'class' => '@string',
|
||||
'attr' => 'array',
|
||||
'divider' => 'bool'
|
||||
];
|
||||
|
||||
case 'TagEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -282,7 +315,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
];
|
||||
|
||||
// embedded meta data
|
||||
// embedded meta data
|
||||
case 'UserPreference':
|
||||
return [
|
||||
'name' => 'string',
|
||||
@@ -298,9 +331,9 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'value' => 'string',
|
||||
];
|
||||
|
||||
// if a user is embedded in other objects
|
||||
// if a user is embedded in other objects
|
||||
case 'User':
|
||||
// if a list of users is loaded
|
||||
// if a list of users is loaded
|
||||
case 'UserCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -309,9 +342,11 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
'alias' => '@string',
|
||||
'accountNumber' => '@string',
|
||||
'initials' => '@string',
|
||||
'title' => '@string',
|
||||
];
|
||||
|
||||
// if a user is loaded explicitly
|
||||
// if a user is loaded explicitly
|
||||
case 'UserEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -323,6 +358,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
'teams' => ['result' => 'array', 'type' => 'Team'],
|
||||
'roles' => ['result' => 'array', 'type' => 'string'],
|
||||
'initials' => 'string',
|
||||
'language' => 'string',
|
||||
'timezone' => 'string',
|
||||
'accountNumber' => '@string',
|
||||
@@ -330,9 +366,9 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'preferences' => ['result' => 'array', 'type' => 'UserPreference'],
|
||||
];
|
||||
|
||||
// if a team is embedded
|
||||
// if a team is embedded
|
||||
case 'Team':
|
||||
// if a collection of teams is requested
|
||||
// if a collection of teams is requested
|
||||
case 'TeamCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -340,35 +376,33 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
];
|
||||
|
||||
// explicitly requested team
|
||||
// explicitly requested team
|
||||
case 'TeamEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
'name' => 'string',
|
||||
'color' => '@string',
|
||||
'teamlead' => ['result' => 'object', 'type' => '@User'],
|
||||
'members' => ['result' => 'array', 'type' => 'TeamMember'],
|
||||
'users' => ['result' => 'array', 'type' => 'User'],
|
||||
'customers' => ['result' => 'array', 'type' => '@Customer'],
|
||||
'projects' => ['result' => 'array', 'type' => '@Project'],
|
||||
'activities' => ['result' => 'array', 'type' => '@Activity'],
|
||||
];
|
||||
|
||||
// if the team is used inside the team context
|
||||
// if the team is used inside the team context
|
||||
case 'TeamMember':
|
||||
return [
|
||||
'user' => ['result' => 'object', 'type' => 'User'],
|
||||
'teamlead' => 'bool',
|
||||
];
|
||||
|
||||
// if the team is used inside the user context
|
||||
// if the team is used inside the user context
|
||||
case 'TeamMembership':
|
||||
return [
|
||||
'team' => ['result' => 'object', 'type' => 'Team'],
|
||||
'teamlead' => 'bool',
|
||||
];
|
||||
|
||||
// if a customer is embedded in other objects
|
||||
// if a customer is embedded in other objects
|
||||
case 'Customer':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -380,7 +414,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'comment' => '@string',
|
||||
];
|
||||
|
||||
// if a list of customers is loaded
|
||||
// if a list of customers is loaded
|
||||
case 'CustomerCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -395,7 +429,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'currency' => 'string', // since 1.10
|
||||
];
|
||||
|
||||
// if a customer is loaded explicitly
|
||||
// if a customer is loaded explicitly
|
||||
case 'CustomerEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -424,7 +458,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'budgetType' => '@string', // since 1.15
|
||||
];
|
||||
|
||||
// if a project is embedded
|
||||
// if a project is embedded
|
||||
case 'Project':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -437,7 +471,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'comment' => '@string',
|
||||
];
|
||||
|
||||
// if a project is embedded in an expanded collection (here timesheet)
|
||||
// if a project is embedded in an expanded collection (here timesheet)
|
||||
case 'ProjectExpanded':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -450,7 +484,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'comment' => '@string',
|
||||
];
|
||||
|
||||
// if a collection of projects is loaded
|
||||
// if a collection of projects is loaded
|
||||
case 'ProjectCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -468,7 +502,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'comment' => '@string',
|
||||
];
|
||||
|
||||
// if a project is explicitly loaded
|
||||
// if a project is explicitly loaded
|
||||
case 'ProjectEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -479,19 +513,19 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'color' => '@string',
|
||||
'metaFields' => ['result' => 'array', 'type' => 'ProjectMeta'],
|
||||
'parentTitle' => 'string',
|
||||
'start' => '@datetime',
|
||||
'end' => '@datetime',
|
||||
'start' => '@date',
|
||||
'end' => '@date',
|
||||
'globalActivities' => 'bool',
|
||||
'teams' => ['result' => 'array', 'type' => 'Team'],
|
||||
'comment' => '@string',
|
||||
'budget' => 'float',
|
||||
'timeBudget' => 'int',
|
||||
'orderNumber' => '@string',
|
||||
'orderDate' => '@datetime',
|
||||
'orderDate' => '@date',
|
||||
'budgetType' => '@string', // since 1.15
|
||||
];
|
||||
|
||||
// embedded activities
|
||||
// embedded activities
|
||||
case 'Activity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -514,7 +548,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'comment' => '@string',
|
||||
];
|
||||
|
||||
// collection of activities
|
||||
// collection of activities
|
||||
case 'ActivityCollection':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -529,7 +563,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'teams' => ['result' => 'array', 'type' => 'Team'],
|
||||
];
|
||||
|
||||
// if a activity is explicitly loaded
|
||||
// if a activity is explicitly loaded
|
||||
case 'ActivityEntity':
|
||||
return [
|
||||
'id' => 'int',
|
||||
@@ -565,10 +599,10 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'billable' => 'bool',
|
||||
'fixedRate' => '@float',
|
||||
'hourlyRate' => '@float',
|
||||
// TODO new fields: billable, category
|
||||
// TODO new fields: category
|
||||
];
|
||||
|
||||
case 'TimesheetEntityFull':
|
||||
case 'TimesheetExpanded':
|
||||
return [
|
||||
'id' => 'int',
|
||||
'begin' => 'DateTime',
|
||||
@@ -579,14 +613,14 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'activity' => ['result' => 'object', 'type' => 'ActivityExpanded'],
|
||||
'project' => ['result' => 'object', 'type' => 'ProjectExpanded'],
|
||||
'tags' => ['result' => 'array', 'type' => 'string'],
|
||||
'user' => 'int',
|
||||
'user' => ['result' => 'object', 'type' => 'User'],
|
||||
'metaFields' => ['result' => 'array', 'type' => 'TimesheetMeta'],
|
||||
'internalRate' => 'float',
|
||||
'exported' => 'bool',
|
||||
'billable' => 'bool',
|
||||
'fixedRate' => '@float',
|
||||
'hourlyRate' => '@float',
|
||||
// TODO new fields: billable, category
|
||||
// TODO new fields: category
|
||||
];
|
||||
|
||||
case 'TimesheetCollection':
|
||||
@@ -618,7 +652,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
'activity' => ['result' => 'object', 'type' => 'Activity'],
|
||||
'project' => ['result' => 'object', 'type' => 'ProjectExpanded'],
|
||||
'tags' => ['result' => 'array', 'type' => 'string'],
|
||||
'user' => 'int',
|
||||
'user' => ['result' => 'object', 'type' => 'User'],
|
||||
'metaFields' => ['result' => 'array', 'type' => 'TimesheetMeta'],
|
||||
'internalRate' => 'float',
|
||||
'exported' => 'bool',
|
||||
@@ -637,7 +671,7 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
* @param array $result
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function assertApiResponseTypeStructure(string $type, array $result)
|
||||
protected function assertApiResponseTypeStructure(string $type, array $result): void
|
||||
{
|
||||
$expected = self::getExpectedResponseStructure($type);
|
||||
$expectedKeys = array_keys($expected);
|
||||
@@ -704,7 +738,12 @@ abstract class APIControllerBaseTest extends ControllerBaseTest
|
||||
}
|
||||
|
||||
if (strtolower($value) === 'datetime') {
|
||||
// TODO
|
||||
$date = \DateTime::createFromFormat('Y-m-d\TH:i:sO', $result[$key]);
|
||||
self::assertInstanceOf(\DateTime::class, $date, sprintf('Field "%s" was expected to be a Date with the format "Y-m-dTH:i:sO", but found: %s', $key, $result[$key]));
|
||||
$value = 'string';
|
||||
} elseif (strtolower($value) === 'date') {
|
||||
$date = \DateTime::createFromFormat('Y-m-d', $result[$key]);
|
||||
self::assertInstanceOf(\DateTime::class, $date, sprintf('Field "%s" was expected to be a Date with the format "Y-m-d", but found: %s', $key, $result[$key]));
|
||||
$value = 'string';
|
||||
}
|
||||
|
||||
|
||||
218
tests/API/ActionsControllerTest.php
Normal file
218
tests/API/ActionsControllerTest.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?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\DataFixtures\ActivityFixtures;
|
||||
use App\Tests\DataFixtures\CustomerFixtures;
|
||||
use App\Tests\DataFixtures\ProjectFixtures;
|
||||
use App\Tests\DataFixtures\TimesheetFixtures;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
*/
|
||||
class ActionsControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
public function testIsSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/actions/timesheet/1/index/en');
|
||||
}
|
||||
|
||||
public function test_getTimesheetActions()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$items = $this->importFixture(new TimesheetFixtures($this->getUserByRole(User::ROLE_USER), 1));
|
||||
|
||||
$views = [
|
||||
'index' => [
|
||||
'repeat',
|
||||
'edit',
|
||||
'copy',
|
||||
'divider0',
|
||||
'trash',
|
||||
],
|
||||
'calendar' => [
|
||||
'repeat',
|
||||
'edit',
|
||||
'copy',
|
||||
'divider0',
|
||||
'trash',
|
||||
],
|
||||
'custom' => [
|
||||
'repeat',
|
||||
'edit',
|
||||
'copy',
|
||||
'divider0',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($views as $view => $entries) {
|
||||
$this->assertAccessIsGranted($client, sprintf('/api/actions/timesheet/%s/%s/en', $items[0]->getId(), $view));
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
foreach ($result as $item) {
|
||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($entries as $id) {
|
||||
self::assertEquals($id, $result[$i]['id'], sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_getActivityActions()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
$customers = $this->importFixture(new CustomerFixtures(1));
|
||||
|
||||
$projectFixture = new ProjectFixtures(1);
|
||||
$projectFixture->setCustomers($customers);
|
||||
$projects = $this->importFixture($projectFixture);
|
||||
|
||||
$activityFixture = new ActivityFixtures(1);
|
||||
$activityFixture->setProjects($projects);
|
||||
$activities = $this->importFixture($activityFixture);
|
||||
|
||||
$views = [
|
||||
'index' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'divider0',
|
||||
'filter',
|
||||
'divider1',
|
||||
'trash',
|
||||
],
|
||||
'custom' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'divider0',
|
||||
'filter',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($views as $view => $entries) {
|
||||
$this->assertAccessIsGranted($client, sprintf('/api/actions/activity/%s/%s/en', $activities[0]->getId(), $view));
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
foreach ($result as $item) {
|
||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($entries as $id) {
|
||||
self::assertEquals($id, $result[$i]['id'], sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_getProjectActions()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
$customers = $this->importFixture(new CustomerFixtures(1));
|
||||
|
||||
$projectFixture = new ProjectFixtures(1);
|
||||
$projectFixture->setCustomers($customers);
|
||||
$projects = $this->importFixture($projectFixture);
|
||||
|
||||
$views = [
|
||||
'index' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'divider0',
|
||||
'filter',
|
||||
'divider1',
|
||||
'report_project_details',
|
||||
'trash',
|
||||
],
|
||||
'custom' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'divider0',
|
||||
'filter',
|
||||
'divider1',
|
||||
'report_project_details',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($views as $view => $entries) {
|
||||
$this->assertAccessIsGranted($client, sprintf('/api/actions/project/%s/%s/en', $projects[0]->getId(), $view));
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
foreach ($result as $item) {
|
||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($entries as $id) {
|
||||
self::assertEquals($id, $result[$i]['id'], sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_getCustomerActions()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
|
||||
|
||||
$customers = $this->importFixture(new CustomerFixtures(1));
|
||||
|
||||
$views = [
|
||||
'index' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'vcard',
|
||||
'divider0',
|
||||
'filter',
|
||||
'divider1',
|
||||
'report',
|
||||
'trash',
|
||||
],
|
||||
'custom' => [
|
||||
'details',
|
||||
'edit',
|
||||
'permissions',
|
||||
'divider0',
|
||||
'filter',
|
||||
'divider1',
|
||||
'report',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($views as $view => $entries) {
|
||||
$this->assertAccessIsGranted($client, sprintf('/api/actions/customer/%s/%s/en', $customers[0]->getId(), $view));
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
foreach ($result as $item) {
|
||||
self::assertApiResponseTypeStructure('PageActionItem', $item);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($entries as $id) {
|
||||
self::assertEquals($id, $result[$i]['id'], sprintf('Failed action "%s" with name "%s" in view "%s"', $i, $id, $view));
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,8 @@ use App\Entity\RateInterface;
|
||||
use App\Entity\User;
|
||||
use App\Repository\ActivityRateRepository;
|
||||
use App\Repository\ActivityRepository;
|
||||
use App\Repository\Query\VisibilityInterface;
|
||||
use App\Tests\Mocks\ActivityTestMetaFieldSubscriberMock;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -141,7 +141,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
/**
|
||||
* @dataProvider getCollectionTestData
|
||||
*/
|
||||
public function testGetCollection($url, $project, $parameters, $expected)
|
||||
public function testGetCollection($url, $project, $parameters, $expected): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$imports = $this->loadActivityTestData();
|
||||
@@ -153,10 +153,16 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
}
|
||||
|
||||
if (\array_key_exists('projects', $parameters)) {
|
||||
if (stripos($parameters['projects'], ',') !== false) {
|
||||
$parameters['projects'] = $projectId . ',' . $projectId;
|
||||
if (!\is_array($parameters['projects'])) {
|
||||
throw new \InvalidArgumentException('projects needs to be an array');
|
||||
}
|
||||
$count = \count($parameters['projects']);
|
||||
if ($count === 2) {
|
||||
$parameters['projects'] = [$projectId, $projectId];
|
||||
} elseif ($count === 1) {
|
||||
$parameters['projects'] = [$projectId];
|
||||
} else {
|
||||
$parameters['projects'] = (string) $projectId;
|
||||
throw new \InvalidArgumentException('Invalid count for projects');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,19 +183,22 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
public function getCollectionTestData()
|
||||
/**
|
||||
* @return \Generator<array<mixed>>
|
||||
*/
|
||||
public function getCollectionTestData(): iterable
|
||||
{
|
||||
yield ['/api/activities', null, [], [[false], [true, 2], [true, 2], [null], [true, 1]]];
|
||||
//yield ['/api/activities', [], [[false], [false], [true, 2], [true, 1], [true, 2]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true'], [[false], [false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => 3], [[false], [false], [false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => '2'], [[false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => 1], [[false], [false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => VisibilityInterface::SHOW_BOTH], [[false], [false], [false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => VisibilityInterface::SHOW_HIDDEN], [[false]]];
|
||||
yield ['/api/activities', null, ['globals' => 'true', 'visible' => VisibilityInterface::SHOW_VISIBLE], [[false], [false]]];
|
||||
yield ['/api/activities', 0, ['project' => '1'], [[false], [false], [true, 1]]];
|
||||
yield ['/api/activities', 1, ['project' => '2', 'projects' => '2', 'visible' => 1], [[true, 2], [true, 2], [false], [false]]];
|
||||
yield ['/api/activities', 1, ['project' => '2', 'projects' => '2,2', 'visible' => '3'], [[true, 2], [true, 2], [true, 2], [false], [false], [false]]];
|
||||
yield ['/api/activities', 1, ['projects' => '2,2', 'visible' => 2], [[true, 2], [false]]];
|
||||
yield ['/api/activities', 1, ['projects' => '2', 'visible' => 2], [[true, 2], [false]]];
|
||||
yield ['/api/activities', 1, ['project' => '2', 'projects' => ['2'], 'visible' => VisibilityInterface::SHOW_VISIBLE], [[true, 2], [true, 2], [false], [false]]];
|
||||
yield ['/api/activities', 1, ['project' => '2', 'projects' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_BOTH], [[true, 2], [true, 2], [true, 2], [false], [false], [false]]];
|
||||
yield ['/api/activities', 1, ['projects' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_HIDDEN], [[true, 2], [false]]];
|
||||
yield ['/api/activities', 1, ['projects' => ['2'], 'visible' => VisibilityInterface::SHOW_HIDDEN], [[true, 2], [false]]];
|
||||
}
|
||||
|
||||
public function testGetCollectionWithQuery()
|
||||
@@ -222,7 +231,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/activities/2');
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/activities/' . PHP_INT_MAX, 'GET', 'App\\Entity\\Activity object not found by the @ParamConverter annotation.');
|
||||
}
|
||||
|
||||
public function testPostAction()
|
||||
@@ -269,10 +278,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/activities', 'POST', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('User cannot create activities', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot create activities');
|
||||
}
|
||||
|
||||
public function testPostActionWithInvalidData()
|
||||
@@ -343,10 +349,7 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/activities/1', 'PATCH', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('User cannot update activity', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot update activity');
|
||||
}
|
||||
|
||||
public function testPatchActionWithUnknownActivity()
|
||||
@@ -375,32 +378,32 @@ class ActivityControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingName()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['value' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['value' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "name" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingValue()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['name' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['name' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "value" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingMetafield()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 500,
|
||||
'message' => 'Unknown meta-field requested'
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/activities/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 404,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new ActivityTestMetaFieldSubscriberMock());
|
||||
static::getContainer()->get('event_dispatcher')->addSubscriber(new ActivityTestMetaFieldSubscriberMock());
|
||||
|
||||
$data = [
|
||||
'name' => 'metatestmock',
|
||||
|
||||
@@ -38,7 +38,7 @@ class ApiDocControllerTest extends ControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
$expectedKeys = ['Activity', 'Default', 'Customer', 'Project', 'Tag', 'Team', 'Timesheet', 'User'];
|
||||
$expectedKeys = ['Actions', 'Activity', 'Default', 'Customer', 'Project', 'Tag', 'Team', 'Timesheet', 'User'];
|
||||
$actual = array_keys($tags);
|
||||
|
||||
sort($actual);
|
||||
@@ -51,7 +51,71 @@ class ApiDocControllerTest extends ControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/doc.json');
|
||||
$this->assertStringContainsString('"title":"Kimai - API Docs"', $client->getResponse()->getContent());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$paths = [
|
||||
'/api/actions/timesheet/{id}/{view}/{locale}',
|
||||
'/api/actions/activity/{id}/{view}/{locale}',
|
||||
'/api/actions/project/{id}/{view}/{locale}',
|
||||
'/api/actions/customer/{id}/{view}/{locale}',
|
||||
'/api/activities',
|
||||
'/api/activities/{id}',
|
||||
'/api/activities/{id}/meta',
|
||||
'/api/activities/{id}/rates',
|
||||
'/api/activities/{id}/rates/{rateId}',
|
||||
'/api/config/timesheet',
|
||||
'/api/customers',
|
||||
'/api/customers/{id}',
|
||||
'/api/customers/{id}/meta',
|
||||
'/api/customers/{id}/rates',
|
||||
'/api/customers/{id}/rates/{rateId}',
|
||||
'/api/projects',
|
||||
'/api/projects/{id}',
|
||||
'/api/projects/{id}/meta',
|
||||
'/api/projects/{id}/rates',
|
||||
'/api/projects/{id}/rates/{rateId}',
|
||||
'/api/ping',
|
||||
'/api/version',
|
||||
'/api/plugins',
|
||||
'/api/tags',
|
||||
'/api/tags/{id}',
|
||||
'/api/teams',
|
||||
'/api/teams/{id}',
|
||||
'/api/teams/{id}/members/{userId}',
|
||||
'/api/teams/{id}/customers/{customerId}',
|
||||
'/api/teams/{id}/projects/{projectId}',
|
||||
'/api/teams/{id}/activities/{activityId}',
|
||||
'/api/timesheets',
|
||||
'/api/timesheets/{id}',
|
||||
'/api/timesheets/recent',
|
||||
'/api/timesheets/active',
|
||||
'/api/timesheets/{id}/stop',
|
||||
'/api/timesheets/{id}/restart',
|
||||
'/api/timesheets/{id}/duplicate',
|
||||
'/api/timesheets/{id}/export',
|
||||
'/api/timesheets/{id}/meta',
|
||||
'/api/users',
|
||||
'/api/users/{id}',
|
||||
'/api/users/me',
|
||||
];
|
||||
|
||||
$this->assertArrayHasKey('openapi', $json);
|
||||
$this->assertEquals('3.0.0', $json['openapi']);
|
||||
$this->assertArrayHasKey('info', $json);
|
||||
$this->assertEquals('Kimai - API Docs', $json['info']['title']);
|
||||
$this->assertEquals('0.7', $json['info']['version']);
|
||||
|
||||
$this->assertArrayHasKey('paths', $json);
|
||||
$this->assertEquals($paths, array_keys($json['paths']));
|
||||
|
||||
$this->assertArrayHasKey('security', $json);
|
||||
$this->assertArrayHasKey('X-AUTH-USER', $json['security'][0]);
|
||||
$this->assertArrayHasKey('X-AUTH-TOKEN', $json['security'][1]);
|
||||
|
||||
$this->assertArrayHasKey('components', $json);
|
||||
$this->assertArrayHasKey('schemas', $json['components']);
|
||||
$this->assertArrayHasKey('securitySchemes', $json['components']);
|
||||
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
$this->assertNotEmpty($result);
|
||||
|
||||
178
tests/API/Authentication/SessionAuthenticatorTest.php
Normal file
178
tests/API/Authentication/SessionAuthenticatorTest.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?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\Authentication;
|
||||
|
||||
use App\API\Authentication\SessionAuthenticator;
|
||||
use App\API\Authentication\TokenAuthenticator;
|
||||
use App\Entity\User;
|
||||
use App\Repository\ApiUserRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
|
||||
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
|
||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Authentication\SessionAuthenticator
|
||||
*/
|
||||
class SessionAuthenticatorTest extends TestCase
|
||||
{
|
||||
private function getSut(bool $verify = true): SessionAuthenticator
|
||||
{
|
||||
$userProvider = $this->createMock(ApiUserRepository::class);
|
||||
$passwordHasherFactory = $this->createMock(PasswordHasherFactoryInterface::class);
|
||||
$passwordHasher = $this->createMock(PasswordHasherInterface::class);
|
||||
$passwordHasher->method('verify')->willReturn($verify);
|
||||
$passwordHasherFactory->method('getPasswordHasher')->willReturn($passwordHasher);
|
||||
|
||||
$token = new TokenAuthenticator($userProvider, $passwordHasherFactory);
|
||||
|
||||
return new SessionAuthenticator($token);
|
||||
}
|
||||
|
||||
public function testSupports()
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
// not supporting because /api path is not the beginning of the URL
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => 'dfghj/api/doc/dfghj']);
|
||||
self::assertFalse($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo']);
|
||||
self::assertTrue($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/doc']);
|
||||
self::assertFalse($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-SESSION' => true]);
|
||||
self::assertFalse($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
self::assertTrue($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => 'bar', 'HTTP_X-AUTH-SESSION' => true]);
|
||||
self::assertFalse($sut->supports($request));
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingAuthHeader()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingToken()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing token header: X-AUTH-TOKEN');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithEmptyToken()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing token header: X-AUTH-TOKEN');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => '']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingUser()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithEmptyUser()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => '', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticate()
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
self::assertInstanceOf(Passport::class, $passport);
|
||||
$badge = $passport->getBadge(UserBadge::class);
|
||||
self::assertInstanceOf(UserBadge::class, $badge);
|
||||
self::assertEquals('foo2', $badge->getUserIdentifier());
|
||||
|
||||
$user = new User();
|
||||
$user->setApiToken('bar2');
|
||||
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
self::assertInstanceOf(CustomCredentials::class, $badge);
|
||||
self::assertFalse($badge->isResolved());
|
||||
$badge->executeCustomChecker($user);
|
||||
self::assertTrue($badge->isResolved());
|
||||
}
|
||||
|
||||
public function testAuthenticateFailsOnMissingApiTokenForUser()
|
||||
{
|
||||
$this->expectException(BadCredentialsException::class);
|
||||
$this->expectExceptionMessage('The user has no activated API account.');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
|
||||
$user = new User();
|
||||
|
||||
/** @var CustomCredentials $badge */
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
$badge->executeCustomChecker($user);
|
||||
}
|
||||
|
||||
public function testAuthenticateFailsOnWrongPassword()
|
||||
{
|
||||
$this->expectException(BadCredentialsException::class);
|
||||
$this->expectExceptionMessage('The presented password is invalid.');
|
||||
|
||||
$sut = $this->getSut(false);
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
|
||||
$user = new User();
|
||||
$user->setApiToken('bar');
|
||||
|
||||
/** @var CustomCredentials $badge */
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
$badge->executeCustomChecker($user);
|
||||
}
|
||||
}
|
||||
175
tests/API/Authentication/TokenAuthenticatorTest.php
Normal file
175
tests/API/Authentication/TokenAuthenticatorTest.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?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\Authentication;
|
||||
|
||||
use App\API\Authentication\TokenAuthenticator;
|
||||
use App\Entity\User;
|
||||
use App\Repository\ApiUserRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
|
||||
use Symfony\Component\PasswordHasher\PasswordHasherInterface;
|
||||
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\CustomCredentials;
|
||||
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Authentication\TokenAuthenticator
|
||||
*/
|
||||
class TokenAuthenticatorTest extends TestCase
|
||||
{
|
||||
private function getSut(bool $verify = true): TokenAuthenticator
|
||||
{
|
||||
$userProvider = $this->createMock(ApiUserRepository::class);
|
||||
$passwordHasherFactory = $this->createMock(PasswordHasherFactoryInterface::class);
|
||||
$passwordHasher = $this->createMock(PasswordHasherInterface::class);
|
||||
$passwordHasher->method('verify')->willReturn($verify);
|
||||
$passwordHasherFactory->method('getPasswordHasher')->willReturn($passwordHasher);
|
||||
|
||||
return new TokenAuthenticator($userProvider, $passwordHasherFactory);
|
||||
}
|
||||
|
||||
public function testSupports()
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
// not supporting because /api path is not the beginning of the URL
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => 'dfghj/api/doc/dfghj']);
|
||||
self::assertFalse($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo']);
|
||||
self::assertTrue($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/doc']);
|
||||
self::assertFalse($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-SESSION' => true]);
|
||||
self::assertTrue($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
self::assertTrue($sut->supports($request));
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => 'bar', 'HTTP_X-AUTH-SESSION' => true]);
|
||||
self::assertTrue($sut->supports($request));
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingAuthHeader()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingToken()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing token header: X-AUTH-TOKEN');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithEmptyToken()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing token header: X-AUTH-TOKEN');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => '']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingUser()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticateWithEmptyUser()
|
||||
{
|
||||
$this->expectException(CustomUserMessageAuthenticationException::class);
|
||||
$this->expectExceptionMessage('Authentication required, missing user header: X-AUTH-USER');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => '', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$sut->authenticate($request);
|
||||
}
|
||||
|
||||
public function testAuthenticate()
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
self::assertInstanceOf(Passport::class, $passport);
|
||||
$badge = $passport->getBadge(UserBadge::class);
|
||||
self::assertInstanceOf(UserBadge::class, $badge);
|
||||
self::assertEquals('foo2', $badge->getUserIdentifier());
|
||||
|
||||
$user = new User();
|
||||
$user->setApiToken('bar2');
|
||||
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
self::assertInstanceOf(CustomCredentials::class, $badge);
|
||||
self::assertFalse($badge->isResolved());
|
||||
$badge->executeCustomChecker($user);
|
||||
self::assertTrue($badge->isResolved());
|
||||
}
|
||||
|
||||
public function testAuthenticateFailsOnMissingApiTokenForUser()
|
||||
{
|
||||
$this->expectException(BadCredentialsException::class);
|
||||
$this->expectExceptionMessage('The user has no activated API account.');
|
||||
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
|
||||
$user = new User();
|
||||
|
||||
/** @var CustomCredentials $badge */
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
$badge->executeCustomChecker($user);
|
||||
}
|
||||
|
||||
public function testAuthenticateFailsOnWrongPassword()
|
||||
{
|
||||
$this->expectException(BadCredentialsException::class);
|
||||
$this->expectExceptionMessage('The presented password is invalid.');
|
||||
|
||||
$sut = $this->getSut(false);
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
|
||||
$user = new User();
|
||||
$user->setApiToken('bar');
|
||||
|
||||
/** @var CustomCredentials $badge */
|
||||
$badge = $passport->getBadge(CustomCredentials::class);
|
||||
$badge->executeCustomChecker($user);
|
||||
}
|
||||
}
|
||||
@@ -16,33 +16,6 @@ use App\Entity\User;
|
||||
*/
|
||||
class ConfigurationControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
public function testIsI18nSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/config/i18n');
|
||||
}
|
||||
|
||||
public function testGetI18n()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/config/i18n', 'GET');
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertEquals(7, \count($result));
|
||||
$this->assertI18nStructure($result);
|
||||
}
|
||||
|
||||
protected function assertI18nStructure(array $result)
|
||||
{
|
||||
$expectedKeys = ['date', 'dateTime', 'duration', 'formDate', 'is24hours', 'time', 'now'];
|
||||
$actual = array_keys($result);
|
||||
sort($actual);
|
||||
sort($expectedKeys);
|
||||
|
||||
$this->assertEquals($expectedKeys, $actual, 'Config structure does not match');
|
||||
}
|
||||
|
||||
public function testIsTimesheetSecure()
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/config/timesheet');
|
||||
@@ -56,13 +29,8 @@ class ConfigurationControllerTest extends APIControllerBaseTest
|
||||
|
||||
$this->assertIsArray($result);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertEquals(6, \count($result));
|
||||
$this->assertTimesheetStructure($result);
|
||||
}
|
||||
|
||||
protected function assertTimesheetStructure(array $result)
|
||||
{
|
||||
$expectedKeys = ['activeEntriesHardLimit', 'activeEntriesSoftLimit', 'defaultBeginTime', 'isAllowFutureTimes', 'isAllowOverlapping', 'trackingMode'];
|
||||
$expectedKeys = ['activeEntriesHardLimit', 'defaultBeginTime', 'isAllowFutureTimes', 'isAllowOverlapping', 'trackingMode'];
|
||||
$this->assertCount(\count($expectedKeys), $result);
|
||||
$actual = array_keys($result);
|
||||
sort($actual);
|
||||
sort($expectedKeys);
|
||||
|
||||
@@ -21,7 +21,6 @@ use App\Entity\User;
|
||||
use App\Repository\CustomerRateRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Tests\Mocks\CustomerTestMetaFieldSubscriberMock;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -63,10 +62,9 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
$customer = $repository->find($id);
|
||||
|
||||
if (null === $customer) {
|
||||
$customer = new Customer();
|
||||
$customer = new Customer('foooo');
|
||||
$customer->setCountry('DE');
|
||||
$customer->setTimezone('Europre/Paris');
|
||||
$customer->setName('foooo');
|
||||
$repository->saveCustomer($customer);
|
||||
}
|
||||
|
||||
@@ -158,8 +156,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
$em->persist($activity);
|
||||
|
||||
// and finally a team
|
||||
$team = new Team();
|
||||
$team->setName('Testing customer 1 team');
|
||||
$team = new Team('Testing customer 1 team');
|
||||
$team->addTeamlead($this->getUserByRole(User::ROLE_USER));
|
||||
$team->addCustomer($customer);
|
||||
$team->addProject($project);
|
||||
@@ -176,7 +173,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/customers/2');
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/customers/' . PHP_INT_MAX, 'GET', 'App\\Entity\\Customer object not found by the @ParamConverter annotation.');
|
||||
}
|
||||
|
||||
public function testPostAction()
|
||||
@@ -230,10 +227,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/customers', 'POST', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('User cannot create customers', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot create customers');
|
||||
}
|
||||
|
||||
public function testPostActionWithInvalidData()
|
||||
@@ -288,10 +282,7 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/customers/1', 'PATCH', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('User cannot update customer', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot update customer');
|
||||
}
|
||||
|
||||
public function testPatchActionWithUnknownActivity()
|
||||
@@ -330,32 +321,32 @@ class CustomerControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingName()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['value' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['value' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "name" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingValue()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['name' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['name' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "value" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingMetafield()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 500,
|
||||
'message' => 'Unknown meta-field requested'
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/customers/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 404,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new CustomerTestMetaFieldSubscriberMock());
|
||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new CustomerTestMetaFieldSubscriberMock());
|
||||
|
||||
$data = [
|
||||
'name' => 'metatestmock',
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?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\Model;
|
||||
|
||||
use App\API\Model\I18nConfig;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Model\I18nConfig
|
||||
*/
|
||||
class I18nConfigTest extends TestCase
|
||||
{
|
||||
public function testSetter()
|
||||
{
|
||||
$sut = new I18nConfig();
|
||||
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setIs24hours(false));
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setDuration('foo'));
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setDate('bar'));
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setDateTime('hello'));
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setFormDate('world'));
|
||||
$this->assertInstanceOf(I18nConfig::class, $sut->setTime('fun'));
|
||||
}
|
||||
}
|
||||
75
tests/API/Model/PageActionTest.php
Normal file
75
tests/API/Model/PageActionTest.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?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\Model;
|
||||
|
||||
use App\API\Model\PageAction;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Model\PageAction
|
||||
*/
|
||||
class PageActionTest extends TestCase
|
||||
{
|
||||
public function testEmptySettings(): void
|
||||
{
|
||||
$options = [];
|
||||
|
||||
$sut = new PageAction('foo', $options);
|
||||
|
||||
$obj = new \ReflectionClass($sut);
|
||||
|
||||
self::assertEquals('foo', $obj->getProperty('id')->getValue($sut));
|
||||
self::assertEquals('foo', $obj->getProperty('title')->getValue($sut));
|
||||
self::assertEquals('', $obj->getProperty('url')->getValue($sut));
|
||||
self::assertEquals('', $obj->getProperty('class')->getValue($sut));
|
||||
self::assertFalse($obj->getProperty('divider')->getValue($sut));
|
||||
self::assertIsArray($obj->getProperty('attr')->getValue($sut));
|
||||
}
|
||||
|
||||
public function testWithSettings(): void
|
||||
{
|
||||
$options = [
|
||||
'title' => 'bar',
|
||||
'url' => 'http://sdkfjhaslkdjfhaskljh',
|
||||
'class' => 'btn-primary',
|
||||
];
|
||||
|
||||
$sut = new PageAction('trash', $options);
|
||||
|
||||
$obj = new \ReflectionClass($sut);
|
||||
|
||||
self::assertEquals('trash', $obj->getProperty('id')->getValue($sut));
|
||||
self::assertEquals('bar', $obj->getProperty('title')->getValue($sut));
|
||||
self::assertEquals('http://sdkfjhaslkdjfhaskljh', $obj->getProperty('url')->getValue($sut));
|
||||
self::assertEquals('btn-primary', $obj->getProperty('class')->getValue($sut));
|
||||
self::assertTrue($obj->getProperty('divider')->getValue($sut));
|
||||
self::assertIsArray($obj->getProperty('attr')->getValue($sut));
|
||||
}
|
||||
|
||||
public function testDivider(): void
|
||||
{
|
||||
$options = [
|
||||
'title' => 'bar',
|
||||
'url' => null,
|
||||
'class' => 'btn-primary',
|
||||
];
|
||||
|
||||
$sut = new PageAction('divider0', $options);
|
||||
|
||||
$obj = new \ReflectionClass($sut);
|
||||
|
||||
self::assertEquals('divider0', $obj->getProperty('id')->getValue($sut));
|
||||
self::assertEquals('bar', $obj->getProperty('title')->getValue($sut));
|
||||
self::assertNull($obj->getProperty('url')->getValue($sut));
|
||||
self::assertEquals('btn-primary', $obj->getProperty('class')->getValue($sut));
|
||||
self::assertTrue($obj->getProperty('divider')->getValue($sut));
|
||||
self::assertIsArray($obj->getProperty('attr')->getValue($sut));
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,18 @@ class TimesheetConfigTest extends TestCase
|
||||
public function testSetter()
|
||||
{
|
||||
$sut = new TimesheetConfig();
|
||||
$sut->setIsAllowFutureTimes(false);
|
||||
$sut->setIsAllowOverlapping(false);
|
||||
$sut->setDefaultBeginTime('08:00');
|
||||
$sut->setTrackingMode('punch');
|
||||
$sut->setActiveEntriesHardLimit(3);
|
||||
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setIsAllowFutureTimes(false));
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setIsAllowOverlapping(false));
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setDefaultBeginTime('08:00'));
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setTrackingMode('punch'));
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setActiveEntriesSoftLimit(2));
|
||||
$this->assertInstanceOf(TimesheetConfig::class, $sut->setActiveEntriesHardLimit(3));
|
||||
$obj = new \ReflectionClass($sut);
|
||||
|
||||
self::assertFalse($obj->getProperty('isAllowFutureTimes')->getValue($sut));
|
||||
self::assertFalse($obj->getProperty('isAllowOverlapping')->getValue($sut));
|
||||
self::assertEquals('08:00', $obj->getProperty('defaultBeginTime')->getValue($sut));
|
||||
self::assertEquals('punch', $obj->getProperty('trackingMode')->getValue($sut));
|
||||
self::assertEquals(3, $obj->getProperty('activeEntriesHardLimit')->getValue($sut));
|
||||
}
|
||||
}
|
||||
|
||||
29
tests/API/Model/VersionTest.php
Normal file
29
tests/API/Model/VersionTest.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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\Model;
|
||||
|
||||
use App\API\Model\Version;
|
||||
use App\Constants;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \App\API\Model\Version
|
||||
*/
|
||||
class VersionTest extends TestCase
|
||||
{
|
||||
public function testValues(): void
|
||||
{
|
||||
$sut = new Version();
|
||||
|
||||
self::assertEquals(Constants::VERSION, $sut->version);
|
||||
self::assertEquals(Constants::VERSION_ID, $sut->versionId);
|
||||
self::assertEquals(Constants::SOFTWARE . ' ' . Constants::VERSION . ' by Kevin Papst.', $sut->copyright);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ use App\Repository\ProjectRateRepository;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\VisibilityInterface;
|
||||
use App\Tests\Mocks\ProjectTestMetaFieldSubscriberMock;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelBrowser;
|
||||
|
||||
/**
|
||||
@@ -112,10 +111,10 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
|
||||
$customer = $em->getRepository(Customer::class)->find(1);
|
||||
|
||||
$customer2 = (new Customer())->setName('first one')->setVisible(false)->setCountry('de')->setTimezone('Europe/Berlin');
|
||||
$customer2 = (new Customer('first one'))->setVisible(false)->setCountry('de')->setTimezone('Europe/Berlin');
|
||||
$em->persist($customer2);
|
||||
|
||||
$customer3 = (new Customer())->setName('second one')->setCountry('at')->setTimezone('Europe/Vienna');
|
||||
$customer3 = (new Customer('second one'))->setCountry('at')->setTimezone('Europe/Vienna');
|
||||
$em->persist($customer3);
|
||||
|
||||
$project = (new Project())->setName('first')->setVisible(false)->setCustomer($customer2);
|
||||
@@ -142,8 +141,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$em->persist($project);
|
||||
|
||||
// and a team
|
||||
$team = new Team();
|
||||
$team->setName('Testing project team');
|
||||
$team = new Team('Testing project team');
|
||||
$team->addTeamlead($this->getUserByRole(User::ROLE_USER));
|
||||
$team->addCustomer($customer);
|
||||
$team->addProject($project);
|
||||
@@ -161,7 +159,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
/**
|
||||
* @dataProvider getCollectionTestData
|
||||
*/
|
||||
public function testGetCollectionWithParams($url, $customer, $parameters, $expected)
|
||||
public function testGetCollectionWithParams($url, $customer, $parameters, $expected): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$imports = $this->loadProjectTestData($client);
|
||||
@@ -171,11 +169,18 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
if ($customerId !== null) {
|
||||
if (\array_key_exists('customer', $parameters)) {
|
||||
$parameters['customer'] = $customerId;
|
||||
} elseif (\array_key_exists('customers', $parameters)) {
|
||||
if (stripos($parameters['customers'], ',') !== false) {
|
||||
$parameters['customers'] = $customerId . ',' . $customerId;
|
||||
}
|
||||
if (\array_key_exists('customers', $parameters)) {
|
||||
if (!\is_array($parameters['customers'])) {
|
||||
throw new \InvalidArgumentException('customers needs to be an array');
|
||||
}
|
||||
$count = \count($parameters['customers']);
|
||||
if ($count === 2) {
|
||||
$parameters['customers'] = [$customerId, $customerId];
|
||||
} elseif ($count === 1) {
|
||||
$parameters['customers'] = [$customerId];
|
||||
} else {
|
||||
$parameters['customers'] = (string) $customerId;
|
||||
throw new \InvalidArgumentException('Invalid count for customers');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,7 +200,10 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
}
|
||||
}
|
||||
|
||||
public function getCollectionTestData()
|
||||
/**
|
||||
* @return \Generator<array<mixed>>
|
||||
*/
|
||||
public function getCollectionTestData(): iterable
|
||||
{
|
||||
// if you wonder why: case-sensitive ordering feels strange ... "Title" > "fifth”
|
||||
yield ['/api/projects', null, [], [[true, 1], [false, 1], [false, 3]]];
|
||||
@@ -203,15 +211,15 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
yield ['/api/projects', 0, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_VISIBLE], [[true, 1], [false, 1]]];
|
||||
yield ['/api/projects', 0, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_BOTH], [[true, 1], [false, 1], [false, 1]]];
|
||||
yield ['/api/projects', 0, ['customer' => '1', 'visible' => VisibilityInterface::SHOW_HIDDEN], [[false, 1]]];
|
||||
// customer is invisible, so nothing should be returned
|
||||
// customer is invisible => query only returns results for VisibilityInterface::SHOW_BOTH
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_VISIBLE], []];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'customers' => '2', 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'customers' => '2,2', 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
// customer is invisible, so nothing should be returned
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11T23:59:59', 'end' => '2030-12-11T23:59:59'], []];
|
||||
yield ['/api/projects', 1, ['customers' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11T23:59:59', 'end' => '2030-12-11T23:59:59'], []];
|
||||
yield ['/api/projects', 1, ['customers' => '2,2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11T23:59:59', 'end' => '2030-12-11T23:59:59'], []];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'customers' => ['2'], 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customers' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_BOTH], [[false, 2], [false, 2]]];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN], []];
|
||||
yield ['/api/projects', 1, ['customer' => '2', 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
yield ['/api/projects', 1, ['customers' => ['2'], 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
yield ['/api/projects', 1, ['customers' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []];
|
||||
}
|
||||
|
||||
public function testGetEntity()
|
||||
@@ -219,8 +227,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
$customer = (new Customer())
|
||||
->setName('first one')
|
||||
$customer = (new Customer('first one'))
|
||||
->setVisible(true)
|
||||
->setCountry('de')
|
||||
->setTimezone('Europe/Berlin')
|
||||
@@ -255,9 +262,9 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
'name' => 'first',
|
||||
'orderNumber' => null,
|
||||
// make sure the timezone is properly applied in serializer (see #1858)
|
||||
'orderDate' => '2019-11-29T14:35:17+1300',
|
||||
'start' => '2020-01-07T18:19:20+1300',
|
||||
'end' => '2021-03-23T00:00:01+1300',
|
||||
'orderDate' => '2019-11-29',
|
||||
'start' => '2020-01-07',
|
||||
'end' => '2021-03-23',
|
||||
'comment' => null,
|
||||
'visible' => true,
|
||||
'budget' => 0.0,
|
||||
@@ -274,7 +281,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/projects/2');
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/projects/' . PHP_INT_MAX, 'GET', 'App\\Entity\\Project object not found by the @ParamConverter annotation.');
|
||||
}
|
||||
|
||||
public function testPostAction()
|
||||
@@ -283,10 +290,9 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'customer' => 1,
|
||||
'visible' => true,
|
||||
'orderDate' => '2018-02-08T13:02:54',
|
||||
'start' => '2019-02-01T19:32:17',
|
||||
'end' => '2020-02-08T21:11:42',
|
||||
'orderDate' => '2018-04-17',
|
||||
'start' => '2019-02-01',
|
||||
'end' => '2020-02-08',
|
||||
'budget' => '999',
|
||||
'timeBudget' => '7200',
|
||||
'orderNumber' => '1234567890/WXYZ/SUBPROJECT/1234/CONTRACT/EMPLOYEE1',
|
||||
@@ -298,11 +304,13 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('ProjectEntity', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertTrue($result['globalActivities']);
|
||||
self::assertEquals('2018-02-08T13:02:54+0000', $result['orderDate']);
|
||||
self::assertEquals('2019-02-01T19:32:17+0000', $result['start']);
|
||||
self::assertEquals('2020-02-08T21:11:42+0000', $result['end']);
|
||||
self::assertEquals('2018-04-17', $result['orderDate']);
|
||||
self::assertEquals('2019-02-01', $result['start']);
|
||||
self::assertEquals('2020-02-08', $result['end']);
|
||||
self::assertEquals('1234567890/WXYZ/SUBPROJECT/1234/CONTRACT/EMPLOYEE1', $result['orderNumber']);
|
||||
self::assertFalse($result['globalActivities']);
|
||||
self::assertFalse($result['billable']);
|
||||
self::assertFalse($result['visible']);
|
||||
}
|
||||
|
||||
public function testPostActionWithOtherFields()
|
||||
@@ -311,7 +319,32 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'customer' => 1,
|
||||
'globalActivities' => '0',
|
||||
'globalActivities' => true,
|
||||
'billable' => 1,
|
||||
'visible' => '',
|
||||
];
|
||||
$this->request($client, '/api/projects', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('ProjectEntity', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['name']);
|
||||
self::assertTrue($result['globalActivities']);
|
||||
self::assertTrue($result['billable']);
|
||||
self::assertTrue($result['visible']);
|
||||
}
|
||||
|
||||
public function testPostActionWithOtherFieldsAndFalse()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'customer' => 1,
|
||||
'globalActivities' => false,
|
||||
'billable' => false,
|
||||
'visible' => false,
|
||||
];
|
||||
$this->request($client, '/api/projects', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -322,15 +355,19 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['name']);
|
||||
self::assertFalse($result['globalActivities']);
|
||||
self::assertFalse($result['billable']);
|
||||
self::assertFalse($result['visible']);
|
||||
}
|
||||
|
||||
public function testPostActionWithOtherFields2()
|
||||
public function testPostActionWithOtherFields3()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$data = [
|
||||
'name' => 'foo',
|
||||
'customer' => 1,
|
||||
'globalActivities' => '1',
|
||||
'globalActivities' => true,
|
||||
'billable' => true,
|
||||
'visible' => true,
|
||||
];
|
||||
$this->request($client, '/api/projects', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -341,6 +378,8 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['name']);
|
||||
self::assertTrue($result['globalActivities']);
|
||||
self::assertTrue($result['billable']);
|
||||
self::assertTrue($result['visible']);
|
||||
}
|
||||
|
||||
public function testPostActionWithLeastFields()
|
||||
@@ -358,6 +397,9 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
self::assertApiResponseTypeStructure('ProjectEntity', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['name']);
|
||||
self::assertFalse($result['globalActivities']);
|
||||
self::assertFalse($result['billable']);
|
||||
self::assertFalse($result['visible']);
|
||||
}
|
||||
|
||||
public function testPostActionWithInvalidUser()
|
||||
@@ -370,10 +412,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/projects', 'POST', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('User cannot create projects', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot create projects');
|
||||
}
|
||||
|
||||
public function testPostActionWithInvalidData()
|
||||
@@ -422,10 +461,7 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/projects/1', 'PATCH', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('User cannot update project', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'User cannot update project');
|
||||
}
|
||||
|
||||
public function testPatchActionWithUnknownActivity()
|
||||
@@ -455,32 +491,32 @@ class ProjectControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingName()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['value' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['value' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "name" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingValue()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['name' => 'X'], [
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['name' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "value" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingMetafield()
|
||||
{
|
||||
return $this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 500,
|
||||
'message' => 'Unknown meta-field requested'
|
||||
$this->assertExceptionForPatchAction(User::ROLE_ADMIN, '/api/projects/1/meta', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 404,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testMetaAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
static::$kernel->getContainer()->get('event_dispatcher')->addSubscriber(new ProjectTestMetaFieldSubscriberMock());
|
||||
self::getContainer()->get('event_dispatcher')->addSubscriber(new ProjectTestMetaFieldSubscriberMock());
|
||||
|
||||
$data = [
|
||||
'name' => 'metatestmock',
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\Entity\ActivityRate;
|
||||
use App\Entity\CustomerRate;
|
||||
use App\Entity\ProjectRate;
|
||||
use App\Entity\RateInterface;
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -41,7 +43,8 @@ trait RateControllerTestTrait
|
||||
'internal_rate' => 6.66,
|
||||
'is_fixed' => false
|
||||
];
|
||||
$this->assertEntityNotFoundForPost(User::ROLE_ADMIN, $this->getRateUrl(99), $data, 'Not found');
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertEntityNotFoundForPost($client, $this->getRateUrl(99), $data);
|
||||
}
|
||||
|
||||
public function testAddRateMissingUserAction()
|
||||
@@ -71,10 +74,7 @@ trait RateControllerTestTrait
|
||||
];
|
||||
$this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('Access denied.', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'Access denied.');
|
||||
}
|
||||
|
||||
public function testAddRateAction()
|
||||
@@ -178,12 +178,14 @@ trait RateControllerTestTrait
|
||||
|
||||
public function testDeleteRateEntityNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, $this->getRateUrl(99, 1));
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, $this->getRateUrl(99, 1));
|
||||
}
|
||||
|
||||
public function testDeleteRateRateNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, $this->getRateUrl(1, 99));
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, $this->getRateUrl(1, 99));
|
||||
}
|
||||
|
||||
public function testDeleteRateWithInvalidAssignment()
|
||||
@@ -198,9 +200,12 @@ trait RateControllerTestTrait
|
||||
public function testDeleteNotAllowed()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importTestRates(1);
|
||||
$rates = $this->importTestRates(1);
|
||||
|
||||
$this->request($client, $this->getRateUrl(1, 1), 'DELETE');
|
||||
/** @var ActivityRate|ProjectRate|CustomerRate $rate */
|
||||
$rate = $rates[0];
|
||||
|
||||
$this->request($client, $this->getRateUrl(1, $rate->getId()), 'DELETE');
|
||||
$this->assertApiResponseAccessDenied($client->getResponse(), 'Access denied.');
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ use JMS\Serializer\JsonSerializationVisitor;
|
||||
use JMS\Serializer\SerializationContext;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\ErrorHandler\Exception\FlattenException;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
@@ -45,9 +46,10 @@ class ValidationFailedExceptionErrorHandlerTest extends TestCase
|
||||
|
||||
public function testWithEmptyConstraintsList()
|
||||
{
|
||||
$security = $this->createMock(Security::class);
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$handler = $this->createMock(FlattenExceptionHandler::class);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler, $security);
|
||||
|
||||
$constraints = new ConstraintViolationList();
|
||||
$validations = new ValidationFailedException($constraints, 'Uuups, that is broken');
|
||||
@@ -64,10 +66,11 @@ class ValidationFailedExceptionErrorHandlerTest extends TestCase
|
||||
|
||||
public function testWithUnsupportedException()
|
||||
{
|
||||
$security = $this->createMock(Security::class);
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$handler = $this->createMock(FlattenExceptionHandler::class);
|
||||
$handler->method('serializeToJson')->willReturn('foooo');
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler, $security);
|
||||
$actual = $sut->serializeExceptionToJson(
|
||||
new JsonSerializationVisitor(),
|
||||
FlattenException::createFromThrowable(new \Exception('sdfsdf')),
|
||||
@@ -80,9 +83,10 @@ class ValidationFailedExceptionErrorHandlerTest extends TestCase
|
||||
|
||||
public function testWithConstraintsList()
|
||||
{
|
||||
$security = $this->createMock(Security::class);
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$handler = $this->createMock(FlattenExceptionHandler::class);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler, $security);
|
||||
$translator->method('trans')->willReturnArgument(0);
|
||||
|
||||
$constraints = new ConstraintViolationList();
|
||||
@@ -123,10 +127,11 @@ class ValidationFailedExceptionErrorHandlerTest extends TestCase
|
||||
|
||||
public function testWithConstraintsListAndWrongException()
|
||||
{
|
||||
$security = $this->createMock(Security::class);
|
||||
$translator = $this->createMock(TranslatorInterface::class);
|
||||
$handler = $this->createMock(FlattenExceptionHandler::class);
|
||||
$handler->method('serializeToJson')->willReturn('foooo');
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler);
|
||||
$sut = new ValidationFailedExceptionErrorHandler($translator, $handler, $security);
|
||||
$translator->method('trans')->willReturnArgument(0);
|
||||
|
||||
$constraints = new ConstraintViolationList();
|
||||
|
||||
@@ -52,18 +52,12 @@ class StatusControllerTest extends APIControllerBaseTest
|
||||
|
||||
$this->assertArrayHasKey('version', $result);
|
||||
$this->assertArrayHasKey('versionId', $result);
|
||||
$this->assertArrayHasKey('candidate', $result);
|
||||
$this->assertArrayHasKey('semver', $result);
|
||||
$this->assertArrayHasKey('name', $result);
|
||||
$this->assertArrayHasKey('copyright', $result);
|
||||
|
||||
$this->assertSame(Constants::VERSION, $result['version']);
|
||||
$this->assertSame(Constants::VERSION_ID, $result['versionId']);
|
||||
$this->assertEquals(Constants::STATUS, $result['candidate']);
|
||||
$this->assertEquals(Constants::VERSION . '-' . Constants::STATUS, $result['semver']);
|
||||
$this->assertEquals(Constants::NAME, $result['name']);
|
||||
$this->assertEquals(
|
||||
'Kimai ' . Constants::VERSION . ' by Kevin Papst and contributors.',
|
||||
'Kimai ' . Constants::VERSION . ' by Kevin Papst.',
|
||||
$result['copyright']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ class TagControllerTest extends APIControllerBaseTest
|
||||
$this->assertApiCallValidationError($response, ['name', 'color']);
|
||||
}
|
||||
|
||||
public function testPostActionWithInvalidUser()
|
||||
public function testPostActionAsRegularUser()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importTagFixtures();
|
||||
@@ -104,11 +104,13 @@ class TagControllerTest extends APIControllerBaseTest
|
||||
'name' => 'foo',
|
||||
];
|
||||
$this->request($client, '/api/tags', 'POST', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('User cannot create tags', $json['message']);
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TagEntity', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['name']);
|
||||
}
|
||||
|
||||
public function testPartOfEntries()
|
||||
@@ -147,6 +149,7 @@ class TagControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testDeleteActionWithUnknownTimesheet()
|
||||
{
|
||||
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/tags/' . PHP_INT_MAX);
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, '/api/tags/' . PHP_INT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,12 +79,13 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testNotFound()
|
||||
{
|
||||
$this->assertEntityNotFound(User::ROLE_ADMIN, '/api/teams/3');
|
||||
$this->assertEntityNotFound(User::ROLE_USER, '/api/teams/' . PHP_INT_MAX, 'GET', 'App\\Entity\\Team object not found by the @ParamConverter annotation.');
|
||||
}
|
||||
|
||||
public function testDeleteActionWithUnknownTeam()
|
||||
{
|
||||
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/teams/255');
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/' . PHP_INT_MAX);
|
||||
}
|
||||
|
||||
public function testPostAction()
|
||||
@@ -114,10 +115,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
self::assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
self::assertEquals('Access denied.', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'Access denied.');
|
||||
}
|
||||
|
||||
public function testPostActionWithValidationErrors()
|
||||
@@ -168,14 +166,14 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertCount(3, $result['users']);
|
||||
self::assertCount(3, $result['members']);
|
||||
|
||||
$this->request($client, '/api/teams/' . $updateId);
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||
self::assertCount(3, $result['users']);
|
||||
self::assertCount(3, $result['members']);
|
||||
|
||||
self::assertFalse($result['members'][1]['teamlead']);
|
||||
self::assertEquals(1, $result['members'][1]['user']['id']);
|
||||
@@ -247,7 +245,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertCount(1, $result['users']);
|
||||
self::assertCount(1, $result['members']);
|
||||
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'POST');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -255,7 +253,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||
self::assertCount(2, $result['users']);
|
||||
self::assertCount(2, $result['members']);
|
||||
}
|
||||
|
||||
public function testPostMemberActionErrors()
|
||||
@@ -273,26 +271,17 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/members/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/members/999');
|
||||
|
||||
// user not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('User not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/' . $result['id'] . '/members/999');
|
||||
|
||||
// add user
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/5', 'POST');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
// cannot add existing member
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/5', 'POST');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('User is already member of the team', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/members/5', 'POST');
|
||||
}
|
||||
|
||||
public function testDeleteMemberAction()
|
||||
@@ -310,7 +299,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$this->request($client, '/api/teams', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertCount(4, $result['users']);
|
||||
self::assertCount(4, $result['members']);
|
||||
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'DELETE');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -318,7 +307,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||
self::assertCount(3, $result['users']);
|
||||
self::assertCount(3, $result['members']);
|
||||
}
|
||||
|
||||
public function testDeleteMemberActionErrors()
|
||||
@@ -338,32 +327,20 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/members/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/999/members/999');
|
||||
|
||||
// user not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('User not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/' . $result['id'] . '/members/999');
|
||||
|
||||
// remove user
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'DELETE');
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
// cannot remove non-member
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/2', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('User is not a member of the team', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/members/2', 'DELETE');
|
||||
|
||||
// cannot remove teamlead
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/members/1', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Cannot remove teamlead', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/members/1', 'DELETE');
|
||||
}
|
||||
|
||||
public function testPostCustomerAction()
|
||||
@@ -405,16 +382,10 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/customers/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/customers/999');
|
||||
|
||||
// customer not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Customer not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/' . $result['id'] . '/customers/999');
|
||||
|
||||
// add customer
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
||||
@@ -423,10 +394,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
self::assertCount(1, $result['customers']);
|
||||
|
||||
// cannot add existing customer
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team has already access to customer', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
||||
}
|
||||
|
||||
public function testDeleteCustomerAction()
|
||||
@@ -478,22 +446,13 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/customers/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/999/customers/999');
|
||||
|
||||
// customer not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Customer not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/' . $result['id'] . '/customers/999');
|
||||
|
||||
// cannot remove customer
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Customer is not assigned to the team', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/customers/1', 'DELETE');
|
||||
}
|
||||
|
||||
public function testPostProjectAction()
|
||||
@@ -534,16 +493,12 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/projects/999');
|
||||
|
||||
$this->request($client, '/api/teams/999/projects/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
|
||||
// project not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Project not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/' . $result['id'] . '/projects/999');
|
||||
|
||||
// add project
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
||||
@@ -552,10 +507,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
self::assertCount(1, $result['projects']);
|
||||
|
||||
// cannot add existing project
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team has already access to project', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
||||
}
|
||||
|
||||
public function testDeleteProjectAction()
|
||||
@@ -607,22 +559,13 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/projects/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/999/projects/999');
|
||||
|
||||
// project not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Project not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/' . $result['id'] . '/projects/999');
|
||||
|
||||
// cannot remove project
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Project is not assigned to the team', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/projects/1', 'DELETE');
|
||||
}
|
||||
|
||||
public function testPostActivityAction()
|
||||
@@ -663,16 +606,10 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/activities/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/999/activities/999');
|
||||
|
||||
// activity not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/999', 'POST');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Activity not found', $json['message']);
|
||||
$this->assertEntityNotFoundForPost($client, '/api/teams/' . $result['id'] . '/activities/999');
|
||||
|
||||
// add activity
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||
@@ -681,10 +618,7 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
self::assertCount(1, $result['activities']);
|
||||
|
||||
// cannot add existing activity
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team has already access to activity', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||
}
|
||||
|
||||
public function testDeleteActivityAction()
|
||||
@@ -736,21 +670,12 @@ class TeamControllerTest extends APIControllerBaseTest
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
// team not found
|
||||
$this->request($client, '/api/teams/999/activities/9999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Team not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/999/activities/9999');
|
||||
|
||||
// activity not found
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/9999', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_NOT_FOUND, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Activity not found', $json['message']);
|
||||
$this->assertNotFoundForDelete($client, '/api/teams/' . $result['id'] . '/activities/9999');
|
||||
|
||||
// cannot remove activity
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'DELETE');
|
||||
self::assertEquals(Response::HTTP_BAD_REQUEST, $client->getResponse()->getStatusCode());
|
||||
$json = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertEquals('Activity is not assigned to the team', $json['message']);
|
||||
$this->assertBadRequest($client, '/api/teams/' . $result['id'] . '/activities/1', 'DELETE');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,16 +32,15 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
public const TEST_TIMEZONE = 'Europe/London';
|
||||
|
||||
/**
|
||||
* @param string $role
|
||||
* @return Timesheet[]
|
||||
*/
|
||||
protected function importFixtureForUser(string $role): array
|
||||
protected function importFixtureForUser(string $role, int $amount = 10): array
|
||||
{
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture
|
||||
->setFixedRate(true)
|
||||
->setHourlyRate(true)
|
||||
->setAmount(10)
|
||||
->setAmount($amount)
|
||||
->setUser($this->getUserByRole($role))
|
||||
->setAllowEmptyDescriptions(false)
|
||||
->setStartDate((new \DateTime('first day of this month'))->setTime(0, 0, 1))
|
||||
@@ -85,7 +84,6 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture
|
||||
@@ -152,11 +150,11 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$end->setTime(23, 59, 59);
|
||||
|
||||
$query = [
|
||||
'customers' => '1',
|
||||
'projects' => '1',
|
||||
'activities' => '1',
|
||||
'customers' => ['1'],
|
||||
'projects' => ['1'],
|
||||
'activities' => ['1'],
|
||||
'page' => 2,
|
||||
'size' => 5,
|
||||
'size' => 4,
|
||||
'order' => 'DESC',
|
||||
'orderBy' => 'rate',
|
||||
'active' => 0,
|
||||
@@ -167,19 +165,19 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER, 22);
|
||||
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
$this->assertPagination($client->getResponse(), 2, 4, 6, 22);
|
||||
$this->assertIsArray($result);
|
||||
$this->assertNotEmpty($result);
|
||||
$this->assertEquals(5, \count($result));
|
||||
$this->assertEquals(4, \count($result));
|
||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||
}
|
||||
|
||||
public function testGetCollectionWithQueryFailsWith404OnOutOfRangedPage()
|
||||
{
|
||||
$modifiedAfter = new \DateTime('-1 hour');
|
||||
$begin = new \DateTime('first day of this month');
|
||||
$begin->setTime(0, 0, 0);
|
||||
$end = new \DateTime('last day of this month');
|
||||
@@ -193,7 +191,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
$this->request($client, '/api/timesheets', 'GET', $query);
|
||||
$this->assertApiException($client->getResponse(), ['code' => 404, 'message' => 'Page "19" does not exist. The currentPage must be inferior to "1"']);
|
||||
$this->assertApiException($client->getResponse(), ['code' => 404, 'message' => 'Not Found']);
|
||||
}
|
||||
|
||||
public function testGetCollectionWithSingleParamsQuery()
|
||||
@@ -426,7 +424,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
$this->assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TimesheetEntityFull', $result);
|
||||
self::assertApiResponseTypeStructure('TimesheetExpanded', $result);
|
||||
$this->assertNotEmpty($result['id']);
|
||||
$this->assertTrue($result['duration'] == 28800 || $result['duration'] == 28860); // 1 minute rounding might be applied
|
||||
$this->assertEquals(2016, $result['rate']);
|
||||
@@ -469,7 +467,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$customer = (new Customer())->setName('foo-bar-1')->setVisible(false)->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer = (new Customer('foo-bar-1'))->setVisible(false)->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$em->persist($customer);
|
||||
$project = (new Project())->setName('foo-bar-2')->setVisible(true)->setCustomer($customer);
|
||||
$em->persist($project);
|
||||
@@ -495,7 +493,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$customer = (new Customer())->setName('foo-bar-1')->setVisible(true)->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer = (new Customer('foo-bar-1'))->setVisible(true)->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$em->persist($customer);
|
||||
$project = (new Project())->setName('foo-bar-2')->setVisible(true)->setCustomer($customer);
|
||||
$em->persist($project);
|
||||
@@ -519,7 +517,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$customer = (new Customer())->setName('foo-bar-1')->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer = (new Customer('foo-bar-1'))->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer->setBillable(false);
|
||||
$em->persist($customer);
|
||||
$project = (new Project())->setName('foo-bar-2')->setCustomer($customer);
|
||||
@@ -549,7 +547,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
$customer = (new Customer())->setName('foo-bar-1')->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer = (new Customer('foo-bar-1'))->setCountry('DE')->setTimezone('Europe/Berlin');
|
||||
$customer->setBillable(false);
|
||||
$em->persist($customer);
|
||||
$project = (new Project())->setName('foo-bar-2')->setCustomer($customer);
|
||||
@@ -627,15 +625,12 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'PATCH', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('Access denied.', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response);
|
||||
}
|
||||
|
||||
public function testPatchActionWithUnknownTimesheet()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/255', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/255', []);
|
||||
}
|
||||
|
||||
public function testInvalidPatchAction()
|
||||
@@ -647,14 +642,14 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
'activity' => 10,
|
||||
'project' => 1,
|
||||
'begin' => (new \DateTime())->format('Y-m-d H:m'),
|
||||
'end' => (new \DateTime('- 7 hours'))->format('Y-m-d H:m'),
|
||||
'end' => (new \DateTime('- 1 hours'))->format('Y-m-d H:m'),
|
||||
'description' => 'foo',
|
||||
];
|
||||
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'PATCH', [], json_encode($data));
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertEquals(400, $response->getStatusCode());
|
||||
$this->assertApiCallValidationError($response, ['end', 'activity']);
|
||||
$this->assertApiCallValidationError($response, ['activity'], false, ['End date must not be earlier then start date.']);
|
||||
}
|
||||
|
||||
// TODO: TEST PATCH FOR EXPORTED TIMESHEET FOR USER WITHOUT PERMISSION IS REJECTED
|
||||
@@ -679,7 +674,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testDeleteActionWithUnknownTimesheet()
|
||||
{
|
||||
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/timesheets/255', 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
$this->assertNotFoundForDelete($client, '/api/timesheets/255');
|
||||
}
|
||||
|
||||
public function testDeleteActionForDifferentUser()
|
||||
@@ -702,10 +698,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$this->request($client, '/api/timesheets/' . $timesheets[0]->getId(), 'DELETE');
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('Access denied.', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response);
|
||||
}
|
||||
|
||||
public function testDeleteActionForExportedRecordIsNotAllowed()
|
||||
@@ -722,7 +715,9 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$em->flush();
|
||||
|
||||
$this->request($client, '/api/timesheets/' . $id, 'DELETE');
|
||||
$this->assertApiResponseAccessDenied($client->getResponse(), 'Access denied.');
|
||||
|
||||
$response = $client->getResponse();
|
||||
$this->assertApiResponseAccessDenied($response);
|
||||
}
|
||||
|
||||
public function testDeleteActionForExportedRecordIsAllowedForAdmin()
|
||||
@@ -744,7 +739,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testGetRecentAction()
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
||||
|
||||
$start = new \DateTime('-10 days');
|
||||
|
||||
@@ -759,7 +754,6 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$query = [
|
||||
'user' => 'all',
|
||||
'size' => 2,
|
||||
'begin' => $start->format(self::DATE_FORMAT_HTML5),
|
||||
];
|
||||
@@ -805,7 +799,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
|
||||
$start = new \DateTime('-8 hours');
|
||||
$start = new \DateTime('-4 hours');
|
||||
|
||||
$fixture = new TimesheetFixtures();
|
||||
$fixture
|
||||
@@ -859,7 +853,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testStopThrowsNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/11/stop', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/11/stop', []);
|
||||
}
|
||||
|
||||
public function testStopNotAllowedForUser()
|
||||
@@ -902,7 +896,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
->setTags(['Test', 'Administration']);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$query = ['tags' => 'Test'];
|
||||
$query = ['tags' => ['Test']];
|
||||
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
@@ -911,7 +905,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$this->assertEquals(10, \count($result));
|
||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||
|
||||
$query = ['tags' => 'Test,Admin'];
|
||||
$query = ['tags' => ['Test', 'Admin']];
|
||||
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
@@ -920,7 +914,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$this->assertEquals(10, \count($result));
|
||||
self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]);
|
||||
|
||||
$query = ['tags' => 'Nothing-2-see,here'];
|
||||
$query = ['tags' => ['Nothing-2-see', 'here']];
|
||||
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
|
||||
@@ -938,7 +932,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
$data = [
|
||||
'description' => 'foo',
|
||||
'tags' => 'another,testing,bar'
|
||||
'tags' => ['another', 'testing', 'bar']
|
||||
];
|
||||
$this->request($client, '/api/timesheets/' . $id, 'PATCH', [], json_encode($data));
|
||||
|
||||
@@ -969,7 +963,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
$data = [
|
||||
'description' => 'foo',
|
||||
'tags' => 'another,testing,bar'
|
||||
'tags' => ['another', 'testing', 'bar']
|
||||
];
|
||||
$this->request($client, '/api/timesheets/' . $id, 'PATCH', [], json_encode($data));
|
||||
|
||||
@@ -1060,7 +1054,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testRestartThrowsNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/42/restart', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_USER, '/api/timesheets/42/restart', []);
|
||||
}
|
||||
|
||||
public function testDuplicateAction()
|
||||
@@ -1099,7 +1093,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testDuplicateThrowsNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/11/duplicate', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/11/duplicate', []);
|
||||
}
|
||||
|
||||
public function testExportAction()
|
||||
@@ -1143,12 +1137,12 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
public function testExportThrowsNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/' . PHP_INT_MAX . '/export', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/' . PHP_INT_MAX . '/export', []);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsNotFound()
|
||||
{
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/' . PHP_INT_MAX . '/meta', [], 'App\\Entity\\Timesheet object not found by the @ParamConverter annotation.');
|
||||
$this->assertEntityNotFoundForPatch(User::ROLE_ADMIN, '/api/timesheets/' . PHP_INT_MAX . '/meta', []);
|
||||
}
|
||||
|
||||
public function testMetaActionThrowsExceptionOnMissingName()
|
||||
@@ -1159,7 +1153,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
|
||||
$this->assertExceptionForMethod($client, '/api/timesheets/' . $id . '/meta', 'PATCH', ['value' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "name" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'message' => 'Bad Request'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1170,8 +1164,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$id = $timesheets[0]->getId();
|
||||
|
||||
$this->assertExceptionForMethod($client, '/api/timesheets/' . $id . '/meta', 'PATCH', ['name' => 'X'], [
|
||||
'code' => 400,
|
||||
'message' => 'Parameter "value" of value "NULL" violated a constraint "This value should not be null."'
|
||||
'code' => 404,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1182,8 +1176,8 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$id = $timesheets[0]->getId();
|
||||
|
||||
$this->assertExceptionForMethod($client, '/api/timesheets/' . $id . '/meta', 'PATCH', ['name' => 'X', 'value' => 'Y'], [
|
||||
'code' => 500,
|
||||
'message' => 'Unknown meta-field requested'
|
||||
'code' => 404,
|
||||
'message' => 'Not Found'
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1192,7 +1186,7 @@ class TimesheetControllerTest extends APIControllerBaseTest
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$timesheets = $this->importFixtureForUser(User::ROLE_USER);
|
||||
$id = $timesheets[0]->getId();
|
||||
static::$container->get('event_dispatcher')->addSubscriber(new TimesheetTestMetaFieldSubscriberMock());
|
||||
static::getContainer()->get('event_dispatcher')->addSubscriber(new TimesheetTestMetaFieldSubscriberMock());
|
||||
|
||||
$data = [
|
||||
'name' => 'metatestmock',
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
namespace App\Tests\API;
|
||||
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @group integration
|
||||
@@ -215,10 +214,7 @@ class UserControllerTest extends APIControllerBaseTest
|
||||
];
|
||||
$this->request($client, '/api/users', 'POST', [], json_encode($data));
|
||||
$response = $client->getResponse();
|
||||
$this->assertFalse($response->isSuccessful());
|
||||
$this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode());
|
||||
$json = json_decode($response->getContent(), true);
|
||||
$this->assertEquals('Access denied.', $json['message']);
|
||||
$this->assertApiResponseAccessDenied($response, 'Access denied.');
|
||||
}
|
||||
|
||||
public function testPatchAction()
|
||||
@@ -229,7 +225,6 @@ class UserControllerTest extends APIControllerBaseTest
|
||||
'email' => 'foo@example.com',
|
||||
'title' => 'asdfghjkl',
|
||||
'plainPassword' => 'foo@example.com',
|
||||
'enabled' => true,
|
||||
'language' => 'ru',
|
||||
'timezone' => 'Europe/Paris',
|
||||
'roles' => [
|
||||
@@ -240,10 +235,11 @@ class UserControllerTest extends APIControllerBaseTest
|
||||
$this->request($client, '/api/users', 'POST', [], json_encode($data));
|
||||
$this->assertTrue($client->getResponse()->isSuccessful());
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertFalse($result['enabled']);
|
||||
|
||||
$data = [
|
||||
'title' => 'qwertzui',
|
||||
'enabled' => false,
|
||||
'enabled' => true,
|
||||
'language' => 'it',
|
||||
'timezone' => 'America/New_York',
|
||||
'roles' => [
|
||||
@@ -259,7 +255,7 @@ class UserControllerTest extends APIControllerBaseTest
|
||||
$this->assertNotEmpty($result['id']);
|
||||
self::assertEquals('foo', $result['username']);
|
||||
self::assertEquals('qwertzui', $result['title']);
|
||||
self::assertFalse($result['enabled']);
|
||||
self::assertTrue($result['enabled']);
|
||||
self::assertEquals('it', $result['language']);
|
||||
self::assertEquals('America/New_York', $result['timezone']);
|
||||
self::assertEquals(['ROLE_TEAMLEAD'], $result['roles']);
|
||||
|
||||
Reference in New Issue
Block a user