Release 2.53 (#5878)
This commit is contained in:
@@ -43,6 +43,17 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase
|
||||
};
|
||||
}
|
||||
|
||||
protected function getAuthenticatedUserId(string $role = User::ROLE_USER): int
|
||||
{
|
||||
return match ($role) {
|
||||
User::ROLE_SUPER_ADMIN => 6,
|
||||
User::ROLE_ADMIN => 5,
|
||||
User::ROLE_TEAMLEAD => 4,
|
||||
User::ROLE_USER => 2,
|
||||
default => throw new \Exception(\sprintf('Unknown role "%s"', $role)),
|
||||
};
|
||||
}
|
||||
|
||||
protected function createUrl(string $url): string
|
||||
{
|
||||
return '/' . ltrim($url, '/');
|
||||
@@ -215,7 +226,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
* @param array<int, string>|array<string, mixed> $failedFields
|
||||
* @param array<int, string>|array<string, string> $failedFields
|
||||
* @param bool $extraFields test for the error "This form should not contain extra fields"
|
||||
* @param array<int, string>|array<string, mixed> $globalError
|
||||
*/
|
||||
@@ -793,6 +804,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase
|
||||
self::assertIsString($subResult);
|
||||
} else {
|
||||
self::assertIsArray($subResult);
|
||||
self::assertIsString($value['type']);
|
||||
|
||||
if ($value['type'][0] === '@') {
|
||||
if (empty($result[$key])) {
|
||||
@@ -807,6 +819,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase
|
||||
break;
|
||||
|
||||
case 'object':
|
||||
self::assertIsString($value['type']);
|
||||
if ($value['type'][0] === '@') {
|
||||
if (empty($result[$key])) {
|
||||
break;
|
||||
|
||||
@@ -220,11 +220,24 @@ class ActivityControllerTest extends APIControllerBaseTestCase
|
||||
self::assertIsArray($result);
|
||||
self::assertNotEmpty($result);
|
||||
self::assertEquals(5, \count($result));
|
||||
|
||||
self::assertIsArray($result[0]);
|
||||
self::assertApiResponseTypeStructure('ActivityCollection', $result[0]);
|
||||
self::assertEquals($imports[0]->getId(), $result[4]['project']);
|
||||
self::assertEquals($imports[1]->getId(), $result[3]['project']);
|
||||
|
||||
self::assertIsArray($result[2]);
|
||||
self::assertApiResponseTypeStructure('ActivityCollection', $result[2]);
|
||||
self::assertArrayHasKey('project', $result[2]);
|
||||
self::assertEquals($imports[1]->getId(), $result[2]['project']);
|
||||
|
||||
self::assertIsArray($result[3]);
|
||||
self::assertApiResponseTypeStructure('ActivityCollection', $result[3]);
|
||||
self::assertArrayHasKey('project', $result[3]);
|
||||
self::assertEquals($imports[1]->getId(), $result[3]['project']);
|
||||
|
||||
self::assertIsArray($result[4]);
|
||||
self::assertApiResponseTypeStructure('ActivityCollection', $result[4]);
|
||||
self::assertArrayHasKey('project', $result[4]);
|
||||
self::assertEquals($imports[0]->getId(), $result[4]['project']);
|
||||
}
|
||||
|
||||
public function testGetEntityIsSecure(): void
|
||||
|
||||
@@ -32,6 +32,7 @@ class ApiDocControllerTest extends AbstractControllerBaseTestCase
|
||||
self::assertStringContainsString('const config = {"basePath":"/api/doc","router":"memory","logo":"/touch-icon-192x192.png","hideInternal":true};', $content);
|
||||
$results = preg_match('/docs\.apiDescriptionDocument\ \=\ (.*)\.spec;/', $content, $matches);
|
||||
self::assertNotFalse($results);
|
||||
self::assertArrayHasKey(1, $matches);
|
||||
$swaggerJson = json_decode($matches[1], true);
|
||||
self::assertIsArray($swaggerJson);
|
||||
self::assertArrayHasKey('spec', $swaggerJson);
|
||||
|
||||
@@ -171,6 +171,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
self::assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TeamEntity', $result);
|
||||
self::assertNotEmpty($result['id']);
|
||||
self::assertIsArray($result['members']);
|
||||
self::assertCount(3, $result['members']);
|
||||
self::assertIsNumeric($updateId);
|
||||
|
||||
@@ -182,18 +183,23 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
self::assertIsArray($result['members']);
|
||||
self::assertCount(3, $result['members']);
|
||||
|
||||
self::assertIsArray($result['members'][0]);
|
||||
self::assertTrue($result['members'][0]['teamlead']);
|
||||
self::assertIsArray($result['members'][0]['user']);
|
||||
self::assertEquals(2, $result['members'][0]['user']['id']);
|
||||
self::assertEquals('john_user', $result['members'][0]['user']['username']);
|
||||
|
||||
self::assertIsArray($result['members'][1]);
|
||||
self::assertFalse($result['members'][1]['teamlead']);
|
||||
self::assertIsArray($result['members'][1]['user']);
|
||||
self::assertEquals(1, $result['members'][1]['user']['id']);
|
||||
self::assertEquals('clara_customer', $result['members'][1]['user']['username']);
|
||||
|
||||
self::assertIsArray($result['members'][2]);
|
||||
self::assertTrue($result['members'][2]['teamlead']);
|
||||
self::assertIsArray($result['members'][2]['user']);
|
||||
self::assertEquals(4, $result['members'][2]['user']['id']);
|
||||
self::assertEquals('tony_teamlead', $result['members'][2]['user']['username']);
|
||||
|
||||
self::assertTrue($result['members'][0]['teamlead']);
|
||||
self::assertEquals(2, $result['members'][0]['user']['id']);
|
||||
self::assertEquals('john_user', $result['members'][0]['user']['username']);
|
||||
}
|
||||
|
||||
public function testPatchActionWithValidationErrors(): void
|
||||
@@ -380,6 +386,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertIsNumeric($result['id']);
|
||||
self::assertIsArray($result['customers']);
|
||||
self::assertCount(0, $result['customers']);
|
||||
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'POST');
|
||||
@@ -422,6 +429,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertIsNumeric($result['id']);
|
||||
self::assertIsArray($result['customers']);
|
||||
self::assertCount(1, $result['customers']);
|
||||
|
||||
// cannot add existing customer
|
||||
@@ -445,6 +453,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertIsNumeric($result['id']);
|
||||
self::assertIsArray($result['customers']);
|
||||
self::assertCount(0, $result['customers']);
|
||||
|
||||
// add customer
|
||||
@@ -453,6 +462,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertIsNumeric($result['id']);
|
||||
self::assertIsArray($result['customers']);
|
||||
self::assertCount(1, $result['customers']);
|
||||
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/customers/1', 'DELETE');
|
||||
@@ -515,6 +525,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertIsNumeric($result['id']);
|
||||
self::assertIsArray($result['projects']);
|
||||
self::assertCount(0, $result['projects']);
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'POST');
|
||||
self::assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -558,6 +569,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertIsNumeric($result['id']);
|
||||
self::assertIsArray($result['projects']);
|
||||
self::assertCount(1, $result['projects']);
|
||||
|
||||
// cannot add existing project
|
||||
@@ -581,6 +593,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertIsNumeric($result['id']);
|
||||
self::assertIsArray($result['projects']);
|
||||
self::assertCount(0, $result['projects']);
|
||||
|
||||
// add project
|
||||
@@ -590,6 +603,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
self::assertIsArray($result);
|
||||
self::assertIsArray($result['projects']);
|
||||
self::assertCount(1, $result['projects']);
|
||||
self::assertIsInt($result['id']);
|
||||
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/projects/1', 'DELETE');
|
||||
self::assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -649,6 +663,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertIsNumeric($result['id']);
|
||||
self::assertIsArray($result['activities']);
|
||||
self::assertCount(0, $result['activities']);
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||
self::assertTrue($client->getResponse()->isSuccessful());
|
||||
@@ -690,6 +705,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertIsNumeric($result['id']);
|
||||
self::assertIsArray($result['activities']);
|
||||
self::assertCount(1, $result['activities']);
|
||||
|
||||
// cannot add existing activity
|
||||
@@ -714,6 +730,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
self::assertIsArray($result);
|
||||
self::assertIsArray($result['activities']);
|
||||
self::assertCount(0, $result['activities']);
|
||||
self::assertIsInt($result['id']);
|
||||
|
||||
// add activity
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'POST');
|
||||
@@ -721,6 +738,7 @@ class TeamControllerTest extends APIControllerBaseTestCase
|
||||
$result = json_decode($client->getResponse()->getContent(), true);
|
||||
self::assertIsArray($result);
|
||||
self::assertIsNumeric($result['id']);
|
||||
self::assertIsArray($result['activities']);
|
||||
self::assertCount(1, $result['activities']);
|
||||
|
||||
$this->request($client, '/api/teams/' . $result['id'] . '/activities/1', 'DELETE');
|
||||
|
||||
@@ -35,13 +35,21 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
/**
|
||||
* @return Timesheet[]
|
||||
*/
|
||||
protected function importFixtureForUser(string $role, int $amount = 10): array
|
||||
protected function importFixtureForUser(User|string $user, int $amount = 10): array
|
||||
{
|
||||
$fixture = new TimesheetFixtures($this->getUserByRole($role), $amount);
|
||||
if (\is_string($user)) {
|
||||
$role = $user;
|
||||
$user = $this->getUserByRole($role);
|
||||
}
|
||||
|
||||
$start = DateTimeFactory::createByUser($user)->createDateTime('first day of this month');
|
||||
$start = $start->setTime(0, 0, 1);
|
||||
|
||||
$fixture = new TimesheetFixtures($user, $amount);
|
||||
$fixture->setFixedRate(true);
|
||||
$fixture->setHourlyRate(true);
|
||||
$fixture->setAllowEmptyDescriptions(false);
|
||||
$fixture->setStartDate((new \DateTime('first day of this month'))->setTime(0, 0, 1));
|
||||
$fixture->setStartDate($start);
|
||||
|
||||
return $this->importFixture($fixture);
|
||||
}
|
||||
@@ -212,18 +220,25 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
|
||||
public function testGetCollectionWithQuery(): void
|
||||
{
|
||||
$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');
|
||||
$end->setTime(23, 59, 59);
|
||||
$role = User::ROLE_USER;
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$user = $this->getUserByRole($role);
|
||||
$factory = DateTimeFactory::createByUser($user);
|
||||
|
||||
$begin = $factory->createDateTime('first day of this month');
|
||||
$begin = $begin->setTime(0, 0, 1);
|
||||
|
||||
$end = $factory->createDateTime('last day of this month');
|
||||
$end = $end->setTime(23, 59, 59);
|
||||
|
||||
$modifiedAfter = $factory->createDateTime('-20 hour');
|
||||
|
||||
$query = [
|
||||
'customers' => ['1'],
|
||||
'projects' => ['1'],
|
||||
'activities' => ['1'],
|
||||
'page' => 2,
|
||||
'size' => 4,
|
||||
'page' => '2',
|
||||
'size' => '4',
|
||||
'order' => 'DESC',
|
||||
'orderBy' => 'rate',
|
||||
'active' => 0,
|
||||
@@ -233,8 +248,7 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
'exported' => 0,
|
||||
];
|
||||
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER, 22);
|
||||
$this->importFixtureForUser($user, 22);
|
||||
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
|
||||
$content = $client->getResponse()->getContent();
|
||||
self::assertIsString($content);
|
||||
@@ -250,11 +264,6 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
|
||||
public function testGetCollectionWithQueryFailsWith404OnOutOfRangedPage(): void
|
||||
{
|
||||
$begin = new \DateTime('first day of this month');
|
||||
$begin->setTime(0, 0, 0);
|
||||
$end = new \DateTime('last day of this month');
|
||||
$end->setTime(23, 59, 59);
|
||||
|
||||
$query = [
|
||||
'page' => 19,
|
||||
'size' => 50,
|
||||
@@ -268,10 +277,16 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
|
||||
public function testGetCollectionWithSingleParamsQuery(): void
|
||||
{
|
||||
$begin = new \DateTime('first day of this month');
|
||||
$begin->setTime(0, 0, 0);
|
||||
$end = new \DateTime('last day of this month');
|
||||
$end->setTime(23, 59, 59);
|
||||
$role = User::ROLE_USER;
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$user = $this->getUserByRole($role);
|
||||
$factory = DateTimeFactory::createByUser($user);
|
||||
|
||||
$begin = $factory->create('first day of this month');
|
||||
$begin = $begin->setTime(0, 0, 1);
|
||||
|
||||
$end = $factory->create('last day of this month');
|
||||
$end = $end->setTime(23, 59, 59);
|
||||
|
||||
$query = [
|
||||
'customer' => '1',
|
||||
@@ -287,8 +302,7 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
'exported' => 0,
|
||||
];
|
||||
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser($user);
|
||||
$this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query);
|
||||
$content = $client->getResponse()->getContent();
|
||||
self::assertIsString($content);
|
||||
@@ -303,19 +317,23 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
|
||||
public function testExportedFilter(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->importFixtureForUser(User::ROLE_USER);
|
||||
$role = User::ROLE_USER;
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
$user = $this->getUserByRole($role);
|
||||
$factory = DateTimeFactory::createByUser($user);
|
||||
$this->importFixtureForUser($user);
|
||||
|
||||
$fixture = new TimesheetFixtures($this->getUserByRole(User::ROLE_USER), 7);
|
||||
$fixture = new TimesheetFixtures($user, 7);
|
||||
$fixture->setExported(true);
|
||||
$fixture->setStartDate(new \DateTime('first day of this month'));
|
||||
$fixture->setAllowEmptyDescriptions(false);
|
||||
$this->importFixture($fixture);
|
||||
|
||||
$begin = new \DateTime('first day of this month');
|
||||
$begin->setTime(0, 0, 0);
|
||||
$end = new \DateTime('last day of this month');
|
||||
$end->setTime(23, 59, 59);
|
||||
$begin = $factory->create('first day of this month');
|
||||
$begin = $begin->setTime(0, 0, 1);
|
||||
|
||||
$end = $factory->create('last day of this month');
|
||||
$end = $end->setTime(23, 59, 59);
|
||||
|
||||
$query = [
|
||||
'page' => 1,
|
||||
|
||||
@@ -403,4 +403,50 @@ class UserControllerTest extends APIControllerBaseTestCase
|
||||
self::assertEquals('another,testing,bar', $user->getPreferenceValue('metatestmock'));
|
||||
self::assertEquals('another,testing,bar', $user->getPreferenceValue('metatestmock'));
|
||||
}
|
||||
|
||||
public function testUpdateUserPreferenceWithEnabledPreference(): void
|
||||
{
|
||||
$role = User::ROLE_SUPER_ADMIN;
|
||||
$id = $this->getAuthenticatedUserId($role);
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
/** @var User $user */
|
||||
$user = $em->getRepository(User::class)->find($id);
|
||||
self::assertEquals(46, $user->getPreferenceValue('hourly_rate'));
|
||||
|
||||
$data = [
|
||||
[
|
||||
'name' => 'hourly_rate',
|
||||
'value' => 99
|
||||
]
|
||||
];
|
||||
$this->request($client, '/api/users/' . $id . '/preferences', 'PATCH', [], (string) json_encode($data));
|
||||
self::assertTrue($client->getResponse()->isSuccessful());
|
||||
|
||||
/** @var User $user */
|
||||
$user = $em->getRepository(User::class)->find($id);
|
||||
self::assertEquals(99, $user->getPreferenceValue('hourly_rate'));
|
||||
}
|
||||
|
||||
public function testUpdateUserPreferenceWithDisabledPreference(): void
|
||||
{
|
||||
$role = User::ROLE_USER;
|
||||
$id = $this->getAuthenticatedUserId($role);
|
||||
$client = $this->getClientForAuthenticatedUser($role);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
/** @var User $user */
|
||||
$user = $em->getRepository(User::class)->find($id);
|
||||
self::assertEquals(82, $user->getPreferenceValue('hourly_rate'));
|
||||
|
||||
$data = [
|
||||
[
|
||||
'name' => 'hourly_rate',
|
||||
'value' => 99
|
||||
]
|
||||
];
|
||||
$this->request($client, '/api/users/' . $id . '/preferences', 'PATCH', [], (string) json_encode($data));
|
||||
self::assertApiResponseAccessDenied($client->getResponse());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,6 +303,7 @@ abstract class AbstractControllerBaseTestCase extends WebTestCase
|
||||
$crawler = $client->request('GET', $this->createUrl($url));
|
||||
$form = $crawler->filter($formSelector)->form();
|
||||
if ($disableValidation) {
|
||||
// needed for setting invalid values e.g. in dropdowns
|
||||
$form->disableValidation();
|
||||
}
|
||||
$result = $client->submit($form, $formData);
|
||||
@@ -313,7 +314,13 @@ abstract class AbstractControllerBaseTestCase extends WebTestCase
|
||||
self::assertEquals(
|
||||
\count($fieldNames),
|
||||
\count($validationErrors),
|
||||
\sprintf('Expected %s validation errors, found %s', \count($fieldNames), \count($validationErrors))
|
||||
\sprintf(
|
||||
'Expected %s validation errors, found %s. Expected: %s. Found: %s.',
|
||||
\count($fieldNames),
|
||||
\count($validationErrors),
|
||||
implode(',', $fieldNames),
|
||||
implode(',', $validationErrors->extract(['_text']))
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($fieldNames as $name) {
|
||||
|
||||
@@ -238,11 +238,11 @@ class UserControllerTest extends AbstractControllerBaseTestCase
|
||||
// invalid fields: username, password_second, email, enabled
|
||||
[
|
||||
'user_create' => [
|
||||
'username' => '',
|
||||
'plainPassword' => ['first' => 'sdfsdf123'],
|
||||
'username' => '', // empty
|
||||
'plainPassword' => ['first' => 'sdfsdf123'], // missing second
|
||||
'alias' => 'ycvyxcb',
|
||||
'title' => '34rtwrtewrt',
|
||||
'email' => '',
|
||||
'email' => '', // empty email
|
||||
]
|
||||
],
|
||||
[
|
||||
@@ -251,15 +251,15 @@ class UserControllerTest extends AbstractControllerBaseTestCase
|
||||
'#user_create_email',
|
||||
]
|
||||
],
|
||||
// invalid fields: username, password, email, enabled
|
||||
// invalid fields: username, password, email
|
||||
[
|
||||
[
|
||||
'user_create' => [
|
||||
'username' => 'x',
|
||||
'plainPassword' => ['first' => 'sdfsdf123', 'second' => 'sdfxxxxxxx'],
|
||||
'alias' => 'ycvyxcb',
|
||||
'title' => '34rtwrtewrt',
|
||||
'email' => 'ydfbvsdfgs',
|
||||
'username' => 'x', // too short
|
||||
'plainPassword' => ['first' => 'sdfsdf123', 'second' => 'sdfxxxxxxx'], // do not match
|
||||
'alias' => 'Boo',
|
||||
'title' => 'Foo',
|
||||
'email' => 'ydfbvsdfgs', // invalid email
|
||||
]
|
||||
],
|
||||
[
|
||||
@@ -283,6 +283,22 @@ class UserControllerTest extends AbstractControllerBaseTestCase
|
||||
'#user_create_plainPassword_first',
|
||||
]
|
||||
],
|
||||
// invalid fields: alias (special chars), title (special chars, too long)
|
||||
[
|
||||
[
|
||||
'user_create' => [
|
||||
'username' => 'test1231',
|
||||
'plainPassword' => ['first' => 'A-Real-Password.1', 'second' => 'A-Real-Password.1'],
|
||||
'alias' => '""ycvyx<cb""',
|
||||
'title' => 'sdfgsdfgsd fgsdf "<34rtwrtewrt>" gsdfg sdfg sdfg sdfg sdfg sdfg sdfg sdfgsdfgsd fgsdf gsdfgsgsdfgsdfgsdfgsdfg sdfg sdfg sdfg sdfg sdfg sdfgsdfgsd fgsdf gsdfgsgsdfgsdfgsdfg', // special chars + too long
|
||||
'email' => 'ydfbvsdfgs@example.com',
|
||||
]
|
||||
],
|
||||
[
|
||||
'#user_create_alias',
|
||||
'#user_create_title',
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ final class TimesheetFixtures implements TestFixture
|
||||
* @param string|\DateTime $date
|
||||
* @return TimesheetFixtures
|
||||
*/
|
||||
public function setStartDate($date): TimesheetFixtures
|
||||
public function setStartDate(string|\DateTime $date): TimesheetFixtures
|
||||
{
|
||||
if (!($date instanceof \DateTime)) {
|
||||
$date = new \DateTime($date);
|
||||
@@ -313,8 +313,8 @@ final class TimesheetFixtures implements TestFixture
|
||||
}
|
||||
|
||||
$start = clone $this->startDate;
|
||||
$start->modify("+ $i days");
|
||||
$start->modify('+ ' . rand(1, 172800) . ' seconds'); // up to 2 days
|
||||
$start = $start->modify("+ $i days");
|
||||
$start = $start->modify('+ ' . rand(1, 172800) . ' seconds'); // up to 2 days
|
||||
|
||||
return $start;
|
||||
}
|
||||
|
||||
@@ -412,7 +412,8 @@ class ConfigurationTest extends TestCase
|
||||
],
|
||||
'mapping' => [],
|
||||
'connection' => [
|
||||
'organization' => []
|
||||
'organization' => [],
|
||||
'cleanupLongRelayState' => false
|
||||
],
|
||||
'provider' => 'default',
|
||||
],
|
||||
|
||||
48
tests/Form/Type/WeekDaysTypeTest.php
Normal file
48
tests/Form/Type/WeekDaysTypeTest.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?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\Form\Type;
|
||||
|
||||
use App\Form\Type\WeekDaysType;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\Test\TypeTestCase;
|
||||
|
||||
#[CoversClass(WeekDaysType::class)]
|
||||
class WeekDaysTypeTest extends TypeTestCase
|
||||
{
|
||||
public function testSubmitNull(): void
|
||||
{
|
||||
$data = ['week_days' => null];
|
||||
$model = new TypeTestModel(['week_days' => null]);
|
||||
|
||||
$form = $this->factory->createBuilder(FormType::class, $model);
|
||||
$form->add('week_days', WeekDaysType::class);
|
||||
$form = $form->getForm();
|
||||
$form->submit($data);
|
||||
|
||||
self::assertTrue($form->isSynchronized());
|
||||
self::assertEquals('', $model->offsetGet('week_days'));
|
||||
}
|
||||
|
||||
public function testSubmitValidData(): void
|
||||
{
|
||||
$data = ['week_days' => ['tuesday', 'friday', 'sunday']];
|
||||
$model = new TypeTestModel(['week_days' => null]);
|
||||
|
||||
$form = $this->factory->createBuilder(FormType::class, $model);
|
||||
$form->add('week_days', WeekDaysType::class);
|
||||
$form = $form->getForm();
|
||||
|
||||
$form->submit($data);
|
||||
|
||||
self::assertTrue($form->isSynchronized());
|
||||
self::assertEquals('tuesday,friday,sunday', $model->offsetGet('week_days'));
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ class DebugRendererTest extends TestCase
|
||||
}
|
||||
|
||||
#[DataProvider('getTestModel')]
|
||||
public function testRender(callable $invoiceModel, $expectedRate, $expectedRows, $expectedDescriptions, $expectedUser1, $expectedUser2, $expectedUser3, $hasProject, $metaFields = []): void
|
||||
public function testRender(callable $invoiceModel, string $expectedRate, int $expectedRows, int $expectedDescriptions, int $expectedUser1, int $expectedUser2, int $expectedUser3, bool $hasProject, array $metaFields = []): void
|
||||
{
|
||||
/** @var InvoiceModel $model */
|
||||
$model = $invoiceModel($this);
|
||||
@@ -67,10 +67,12 @@ class DebugRendererTest extends TestCase
|
||||
|
||||
$this->assertModelStructure($data['model'], \count($model->getQuery()->getProjects()), \count($model->getQuery()->getActivities()));
|
||||
$rows = $data['entries'];
|
||||
self::assertIsArray($rows);
|
||||
self::assertEquals($expectedRows, \count($rows));
|
||||
|
||||
$i = 0;
|
||||
foreach ($rows as $row) {
|
||||
self::assertIsArray($row);
|
||||
$meta = isset($metaFields[$i]) ? $metaFields[$i++] : [];
|
||||
$this->assertEntryStructure($row, $meta);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,11 @@ class SystemConfigurationFactory
|
||||
foreach ($iterator as $value) {
|
||||
$keys = [];
|
||||
foreach (range(0, $iterator->getDepth()) as $depth) {
|
||||
$keys[] = $iterator->getSubIterator($depth)->key();
|
||||
$key = $iterator->getSubIterator($depth)->key();
|
||||
if (!\is_scalar($key)) {
|
||||
continue;
|
||||
}
|
||||
$keys[] = $key;
|
||||
}
|
||||
$newConfig[implode('.', $keys)] = $value;
|
||||
}
|
||||
|
||||
@@ -22,12 +22,37 @@ use Symfony\Component\Security\Http\HttpUtils;
|
||||
#[CoversClass(SamlAuthenticationSuccessHandler::class)]
|
||||
class SamlAuthenticationSuccessHandlerTest extends TestCase
|
||||
{
|
||||
public function testRelayStateWithInvalidHost(): void
|
||||
{
|
||||
$handler = new SamlAuthenticationSuccessHandler(new HttpUtils($this->getUrlGenerator()));
|
||||
$response = $handler->onAuthenticationSuccess($this->getRequest('http://example.com/sso/login', 'https://localhost/relayed'), $this->getSamlToken());
|
||||
self::assertInstanceOf(RedirectResponse::class, $response);
|
||||
$target = $response->getTargetUrl();
|
||||
$expected = 'http://example.com/';
|
||||
self::assertEquals($expected, $target);
|
||||
self::assertTrue($response->isRedirect($target));
|
||||
}
|
||||
|
||||
public function testRelayState(): void
|
||||
{
|
||||
$handler = new SamlAuthenticationSuccessHandler(new HttpUtils($this->getUrlGenerator()));
|
||||
$response = $handler->onAuthenticationSuccess($this->getRequest('/sso/login', 'http://localhost/relayed'), $this->getSamlToken());
|
||||
$response = $handler->onAuthenticationSuccess($this->getRequest('/sso/login', '/relayed'), $this->getSamlToken());
|
||||
self::assertInstanceOf(RedirectResponse::class, $response);
|
||||
self::assertTrue($response->isRedirect('http://localhost/relayed'));
|
||||
$target = $response->getTargetUrl();
|
||||
$expected = 'http://localhost/relayed';
|
||||
self::assertEquals($expected, $target);
|
||||
self::assertTrue($response->isRedirect($target));
|
||||
}
|
||||
|
||||
public function testRelayStateWithFullUrls(): void
|
||||
{
|
||||
$handler = new SamlAuthenticationSuccessHandler(new HttpUtils($this->getUrlGenerator()));
|
||||
$response = $handler->onAuthenticationSuccess($this->getRequest('http://example.com/sso/login', 'http://example.com/relayed/123'), $this->getSamlToken());
|
||||
self::assertInstanceOf(RedirectResponse::class, $response);
|
||||
$target = $response->getTargetUrl();
|
||||
$expected = 'http://example.com/relayed/123';
|
||||
self::assertEquals($expected, $target);
|
||||
self::assertTrue($response->isRedirect($target));
|
||||
}
|
||||
|
||||
public function testWithoutRelayState(): void
|
||||
@@ -71,7 +96,7 @@ class SamlAuthenticationSuccessHandlerTest extends TestCase
|
||||
$params['RelayState'] = $relayState;
|
||||
}
|
||||
|
||||
return Request::create($path, 'get', $params);
|
||||
return Request::create($path, 'post', $params);
|
||||
}
|
||||
|
||||
private function getSamlToken(): SamlToken
|
||||
|
||||
52
tests/Twig/ContractExtensionsTest.php
Normal file
52
tests/Twig/ContractExtensionsTest.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?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\Twig;
|
||||
|
||||
use App\Twig\ContractExtensions;
|
||||
use App\WorkingTime\WorkingTimeService;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Twig\TwigTest;
|
||||
|
||||
#[CoversClass(ContractExtensions::class)]
|
||||
class ContractExtensionsTest extends TestCase
|
||||
{
|
||||
private function getSut(): ContractExtensions
|
||||
{
|
||||
$service = $this->createMock(WorkingTimeService::class);
|
||||
|
||||
$sut = new ContractExtensions($service);
|
||||
|
||||
return $sut;
|
||||
}
|
||||
|
||||
public function testDefinedMethods(): void
|
||||
{
|
||||
self::assertCount(1, $this->getSut()->getTests());
|
||||
self::assertCount(0, $this->getSut()->getFilters());
|
||||
self::assertCount(0, $this->getSut()->getFunctions());
|
||||
}
|
||||
|
||||
public function testGetTests(): void
|
||||
{
|
||||
$filters = [
|
||||
'work_day',
|
||||
];
|
||||
$i = 0;
|
||||
|
||||
$sut = $this->getSut();
|
||||
$twigFilters = $sut->getTests();
|
||||
|
||||
foreach ($twigFilters as $filter) {
|
||||
self::assertInstanceOf(TwigTest::class, $filter);
|
||||
self::assertEquals($filters[$i++], $filter->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,8 @@ class StrictPolicyTestCase extends TestCase
|
||||
[new User(), 'getPlainPassword', 'Tried to access user secrets'],
|
||||
[new User(), 'getConfirmationToken', 'Tried to access user secrets'],
|
||||
[new User(), 'getTotpAuthenticationConfiguration', 'Tried to access user secrets'],
|
||||
[new User(), 'getPlainApiToken', 'Tried to access user secrets'],
|
||||
[new User(), 'getApiToken', 'Tried to access user secrets'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?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\Validator\Constraints;
|
||||
|
||||
use App\Validator\Constraints\NoHtmlSpecialCharacters;
|
||||
use App\Validator\Constraints\NoHtmlSpecialCharactersValidator;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
|
||||
|
||||
/**
|
||||
* @extends ConstraintValidatorTestCase<NoHtmlSpecialCharactersValidator>
|
||||
*/
|
||||
#[CoversClass(NoHtmlSpecialCharacters::class)]
|
||||
#[CoversClass(NoHtmlSpecialCharactersValidator::class)]
|
||||
class NoHtmlSpecialCharactersValidatorTest extends ConstraintValidatorTestCase
|
||||
{
|
||||
protected function createValidator(): NoHtmlSpecialCharactersValidator
|
||||
{
|
||||
return new NoHtmlSpecialCharactersValidator();
|
||||
}
|
||||
|
||||
public function testConstraintIsInvalid(): void
|
||||
{
|
||||
$this->expectException(UnexpectedTypeException::class);
|
||||
|
||||
$this->validator->validate('foo', new NotBlank());
|
||||
}
|
||||
|
||||
public function testGetTargets(): void
|
||||
{
|
||||
$constraint = new NoHtmlSpecialCharacters();
|
||||
self::assertEquals('property', $constraint->getTargets());
|
||||
}
|
||||
|
||||
public static function getValidTestData(): array
|
||||
{
|
||||
return [
|
||||
[''],
|
||||
[null],
|
||||
['asdf-.,123!§$%&/()=?`4567\'890ß'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('getValidTestData')]
|
||||
public function testValidInput(string|null $data): void
|
||||
{
|
||||
$this->validator = $this->createValidator();
|
||||
$this->validator->initialize($this->context);
|
||||
|
||||
$this->validator->validate($data, new NoHtmlSpecialCharacters());
|
||||
|
||||
$this->assertNoViolation();
|
||||
}
|
||||
|
||||
public static function getInvalidTestData(): array
|
||||
{
|
||||
return [
|
||||
['Test" onclick="alert(1)"'],
|
||||
['Test><a href=#>Foo</a>'],
|
||||
['Test" broken string'],
|
||||
];
|
||||
}
|
||||
|
||||
#[DataProvider('getInvalidTestData')]
|
||||
public function testInvalidInput(string|null $data): void
|
||||
{
|
||||
$this->validator = $this->createValidator();
|
||||
$this->validator->initialize($this->context);
|
||||
|
||||
$this->validator->validate($data, new NoHtmlSpecialCharacters());
|
||||
|
||||
$this->buildViolation('These characters are not allowed: {{ chars }}')
|
||||
->setCode(NoHtmlSpecialCharacters::SPECIAL_CHARACTERS_FOUND)
|
||||
->setParameter('{{ chars }}', '< " >')
|
||||
->assertRaised();
|
||||
}
|
||||
}
|
||||
@@ -107,11 +107,6 @@ parameters:
|
||||
count: 1
|
||||
path: API/APIControllerBaseTestCase.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$haystack of function stripos expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: API/APIControllerBaseTestCase.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
|
||||
count: 3
|
||||
@@ -122,11 +117,6 @@ parameters:
|
||||
count: 1
|
||||
path: API/APIControllerBaseTestCase.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$string of function explode expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
path: API/APIControllerBaseTestCase.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#"
|
||||
count: 1
|
||||
@@ -1461,46 +1451,6 @@ parameters:
|
||||
count: 1
|
||||
path: Invoice/Renderer/DebugRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\DebugRendererTest\\:\\:testRender\\(\\) has parameter \\$expectedDescriptions with no type specified\\.$#"
|
||||
count: 1
|
||||
path: Invoice/Renderer/DebugRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\DebugRendererTest\\:\\:testRender\\(\\) has parameter \\$expectedRate with no type specified\\.$#"
|
||||
count: 1
|
||||
path: Invoice/Renderer/DebugRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\DebugRendererTest\\:\\:testRender\\(\\) has parameter \\$expectedRows with no type specified\\.$#"
|
||||
count: 1
|
||||
path: Invoice/Renderer/DebugRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\DebugRendererTest\\:\\:testRender\\(\\) has parameter \\$expectedUser1 with no type specified\\.$#"
|
||||
count: 1
|
||||
path: Invoice/Renderer/DebugRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\DebugRendererTest\\:\\:testRender\\(\\) has parameter \\$expectedUser2 with no type specified\\.$#"
|
||||
count: 1
|
||||
path: Invoice/Renderer/DebugRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\DebugRendererTest\\:\\:testRender\\(\\) has parameter \\$expectedUser3 with no type specified\\.$#"
|
||||
count: 1
|
||||
path: Invoice/Renderer/DebugRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\DebugRendererTest\\:\\:testRender\\(\\) has parameter \\$hasProject with no type specified\\.$#"
|
||||
count: 1
|
||||
path: Invoice/Renderer/DebugRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Method App\\\\Tests\\\\Invoice\\\\Renderer\\\\DebugRendererTest\\:\\:testRender\\(\\) has parameter \\$metaFields with no type specified\\.$#"
|
||||
count: 1
|
||||
path: Invoice/Renderer/DebugRendererTest.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#"
|
||||
count: 1
|
||||
|
||||
Reference in New Issue
Block a user