[3.0] Update Symfony to 7.4 and dependencies, removed deprecated code, removed API password (#5622)
* deactivate deprecated auto-mapping * use native month-picker with fallback on old browser * bump dependencies to new major versions * replace deprecated ARRAY column type with JSON * named arguments * nullable arguments * native lazy ghosts * update routes to use php config * disable PHP < 8.4 * use static RTL configuration * new qr code package * clear out env file * recipe updates * fix deprecations * preset new env variable if not existing * bump maria db version examples * remove deprecated api-token support * fix validator deprecations * remove timesheet category column * fix broken validator autoconfiguration * stabilize tests * fix migration syntax compatibility * fix doctrine deprecation * fix datetime format * remove constructor dependency * only include visible preferences in invoice templates * fix test container dependency * removed ProjectConstraint multi-constraint logic * allow to disable plugins in certain environments * refactor TimesheetConstraint to non-service * activate new interface methods * prevent update issues with .env * improved customer fixture * removed $user->isExportDecimal()
This commit is contained in:
@@ -214,7 +214,6 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
@@ -231,7 +230,7 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase
|
||||
}
|
||||
|
||||
if (\count($globalError) > 0) {
|
||||
self::assertArrayHasKey('errors', $result['errors']);
|
||||
self::assertArrayHasKey('errors', $result['errors'], 'API response has no global errors, but expected ' . \count($globalError));
|
||||
foreach ($globalError as $err) {
|
||||
self::assertTrue(\in_array($err, $result['errors']['errors']), 'Missing global validation error: ' . $err); // @phpstan-ignore binaryOp.invalid
|
||||
}
|
||||
@@ -333,18 +332,13 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase
|
||||
|
||||
// embedded meta data
|
||||
case 'UserPreference':
|
||||
return [
|
||||
'name' => 'string',
|
||||
'value' => '@string',
|
||||
];
|
||||
|
||||
case 'CustomerMeta':
|
||||
case 'ProjectMeta':
|
||||
case 'ActivityMeta':
|
||||
case 'TimesheetMeta':
|
||||
return [
|
||||
'name' => 'string',
|
||||
'value' => 'string',
|
||||
'value' => '@string',
|
||||
];
|
||||
|
||||
// if a user is embedded in other objects
|
||||
@@ -356,7 +350,6 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase
|
||||
'username' => 'string',
|
||||
'email' => 'string',
|
||||
'enabled' => 'bool',
|
||||
'apiToken' => 'bool',
|
||||
'systemAccount' => 'bool',
|
||||
'color' => 'string',
|
||||
'avatar' => '@string',
|
||||
@@ -376,7 +369,6 @@ abstract class APIControllerBaseTestCase extends AbstractControllerBaseTestCase
|
||||
'username' => 'string',
|
||||
'email' => 'string',
|
||||
'enabled' => 'bool',
|
||||
'apiToken' => 'bool',
|
||||
'systemAccount' => 'bool',
|
||||
'color' => 'string',
|
||||
'avatar' => '@string',
|
||||
|
||||
@@ -50,10 +50,10 @@ class ApiDocControllerTest extends AbstractControllerBaseTestCase
|
||||
$expectedKeys = ['Actions', 'Activity', 'Default', 'Customer', 'Project', 'Tag', 'Team', 'Timesheet', 'User', 'Invoice', 'Export'];
|
||||
$actual = array_keys($tags);
|
||||
|
||||
sort($actual);
|
||||
sort($expectedKeys);
|
||||
|
||||
self::assertEquals($expectedKeys, $actual, \sprintf('Expected %s sections in API docs, but found %s.', \count($actual), \count($expectedKeys)));
|
||||
//dd($expectedKeys, $actual);
|
||||
foreach ($expectedKeys as $expectedKey) {
|
||||
self::assertTrue(\in_array($expectedKey, $actual), 'Expected API section is missing: ' . $expectedKey);
|
||||
}
|
||||
|
||||
$paths = [
|
||||
'/api/actions/timesheet/{id}/{view}/{locale}',
|
||||
@@ -115,7 +115,9 @@ class ApiDocControllerTest extends AbstractControllerBaseTestCase
|
||||
self::assertEquals('1.1', $json['info']['version']);
|
||||
|
||||
self::assertArrayHasKey('paths', $json);
|
||||
self::assertEquals($paths, array_keys($json['paths']));
|
||||
foreach ($paths as $path) {
|
||||
self::assertArrayHasKey($path, $json['paths'], 'Missing API endpoint: ' . $path);
|
||||
}
|
||||
|
||||
self::assertArrayHasKey('security', $json);
|
||||
self::assertEquals(['bearer' => []], $json['security'][0]);
|
||||
|
||||
@@ -1,168 +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\Authentication;
|
||||
|
||||
use App\API\Authentication\TokenAuthenticator;
|
||||
use App\Entity\User;
|
||||
use App\Repository\ApiUserRepository;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
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;
|
||||
|
||||
#[CoversClass(TokenAuthenticator::class)]
|
||||
#[Group('legacy')]
|
||||
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(): void
|
||||
{
|
||||
$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::assertFalse($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-USER' => 'foo', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
self::assertTrue($sut->supports($request));
|
||||
}
|
||||
|
||||
public function testAuthenticateWithMissingAuthHeader(): void
|
||||
{
|
||||
$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(): void
|
||||
{
|
||||
$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(): void
|
||||
{
|
||||
$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(): void
|
||||
{
|
||||
$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(): void
|
||||
{
|
||||
$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(): void
|
||||
{
|
||||
$sut = $this->getSut();
|
||||
|
||||
$request = new Request([], [], [], [], [], ['REQUEST_URI' => '/api/fooo', 'HTTP_X-AUTH-USER' => 'foo2', 'HTTP_X-AUTH-TOKEN' => 'bar']);
|
||||
$passport = $sut->authenticate($request);
|
||||
$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(): void
|
||||
{
|
||||
$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(): void
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +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;
|
||||
|
||||
use App\DataFixtures\UserFixtures;
|
||||
use App\Entity\User;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Make sure that the deprecated API login with X-AUTH-USER and X-AUTH-TOKEN still works.
|
||||
*/
|
||||
#[Group('legacy')]
|
||||
#[Group('integration')]
|
||||
class AuthenticationTest extends APIControllerBaseTestCase
|
||||
{
|
||||
public function testPinIsSecure(): void
|
||||
{
|
||||
$this->assertUrlIsSecured('/api/ping');
|
||||
}
|
||||
|
||||
public function testPingWithAccessToken(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$this->assertAccessIsGranted($client, '/api/ping');
|
||||
$response = $client->getResponse()->getContent();
|
||||
self::assertIsString($response);
|
||||
$result = json_decode($response, true);
|
||||
|
||||
self::assertIsArray($result);
|
||||
self::assertEquals(['message' => 'pong'], $result);
|
||||
}
|
||||
|
||||
public function testPingWithAuthTokenAndUsername(): void
|
||||
{
|
||||
$client = self::createClient([], $this->getAuthHeader(UserFixtures::USERNAME_USER, UserFixtures::DEFAULT_API_TOKEN));
|
||||
|
||||
$this->assertAccessIsGranted($client, '/api/ping');
|
||||
$response = $client->getResponse()->getContent();
|
||||
self::assertIsString($response);
|
||||
$result = json_decode($response, true);
|
||||
|
||||
self::assertIsArray($result);
|
||||
self::assertEquals(['message' => 'pong'], $result);
|
||||
}
|
||||
|
||||
public function testPingWithInvalidAuthTokenAndUsername(): void
|
||||
{
|
||||
$client = self::createClient([], $this->getAuthHeader(UserFixtures::USERNAME_USER, 'xxxx'));
|
||||
$url = '/api/ping';
|
||||
$method = 'GET';
|
||||
|
||||
$this->request($client, $url, $method);
|
||||
$response = $client->getResponse();
|
||||
|
||||
$data = [
|
||||
'message' => 'Invalid credentials',
|
||||
];
|
||||
|
||||
self::assertIsString($response->getContent());
|
||||
|
||||
self::assertEquals(
|
||||
$data,
|
||||
json_decode($response->getContent(), true),
|
||||
\sprintf('The secure URL %s is not protected.', $url)
|
||||
);
|
||||
|
||||
self::assertEquals(
|
||||
Response::HTTP_FORBIDDEN,
|
||||
$response->getStatusCode(),
|
||||
\sprintf('The secure URL %s has the wrong status code %s.', $url, $response->getStatusCode())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function getAuthHeader(string $username, string $password): array
|
||||
{
|
||||
return [
|
||||
'HTTP_X_AUTH_USER' => $username,
|
||||
'HTTP_X_AUTH_TOKEN' => $password,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,9 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
#[Group('integration')]
|
||||
class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
{
|
||||
public const DATE_FORMAT = 'Y-m-d H:i:s';
|
||||
public const DATE_FORMAT_HTML5 = 'Y-m-d\TH:i:s';
|
||||
public const TEST_TIMEZONE = 'Europe/London';
|
||||
public const string DATE_FORMAT = 'Y-m-d H:i:s';
|
||||
public const string DATE_FORMAT_HTML5 = 'Y-m-d\TH:i:s';
|
||||
public const string TEST_TIMEZONE = 'Europe/London';
|
||||
|
||||
/**
|
||||
* @return Timesheet[]
|
||||
@@ -470,8 +470,8 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
$data = [
|
||||
'activity' => 1,
|
||||
'project' => 1,
|
||||
'begin' => ($dateTime->createDateTime('-8 hours'))->format('Y-m-d H:m:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:m:0'),
|
||||
'begin' => ($dateTime->createDateTime('-8 hours'))->format('Y-m-d H:i:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:i:0'),
|
||||
'description' => 'foo',
|
||||
'fixedRate' => 2016,
|
||||
'hourlyRate' => 127,
|
||||
@@ -501,8 +501,8 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
$data = [
|
||||
'activity' => 1,
|
||||
'project' => 1,
|
||||
'begin' => ($dateTime->createDateTime('-8 hours'))->format('Y-m-d H:m:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:m:0'),
|
||||
'begin' => ($dateTime->createDateTime('-8 hours'))->format('Y-m-d H:i:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:i:0'),
|
||||
'description' => 'foo',
|
||||
'fixedRate' => 2016,
|
||||
'hourlyRate' => 127,
|
||||
@@ -538,8 +538,8 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
'activity' => 1,
|
||||
'project' => 1,
|
||||
'user' => $user->getId(),
|
||||
'begin' => ($dateTime->createDateTime('- 8 hours'))->format('Y-m-d H:m:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:m:0'),
|
||||
'begin' => ($dateTime->createDateTime('- 8 hours'))->format('Y-m-d H:i:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:i:0'),
|
||||
'description' => 'foo',
|
||||
];
|
||||
$json = json_encode($data);
|
||||
@@ -585,8 +585,8 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
$data = [
|
||||
'activity' => $activity->getId(),
|
||||
'project' => $project->getId(),
|
||||
'begin' => (new \DateTime('- 8 hours'))->format('Y-m-d H:m:s'),
|
||||
'end' => (new \DateTime())->format('Y-m-d H:m:s'),
|
||||
'begin' => (new \DateTime('- 8 hours'))->format('Y-m-d H:i:s'),
|
||||
'end' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||
'description' => 'foo',
|
||||
];
|
||||
$json = json_encode($data);
|
||||
@@ -612,8 +612,8 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
$em->persist($project);
|
||||
|
||||
$data = [
|
||||
'begin' => (new \DateTime('- 8 hours'))->format('Y-m-d H:m:s'),
|
||||
'end' => (new \DateTime())->format('Y-m-d H:m:s'),
|
||||
'begin' => (new \DateTime('- 8 hours'))->format('Y-m-d H:i:s'),
|
||||
'end' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||
'project' => $project->getId(),
|
||||
'activity' => 99,
|
||||
];
|
||||
@@ -635,8 +635,8 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
$em->flush();
|
||||
|
||||
$data = [
|
||||
'begin' => (new \DateTime('- 8 hours'))->format('Y-m-d H:m:s'),
|
||||
'end' => (new \DateTime())->format('Y-m-d H:m:s'),
|
||||
'begin' => (new \DateTime('- 8 hours'))->format('Y-m-d H:i:s'),
|
||||
'end' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||
'project' => 99,
|
||||
'activity' => $activity->getId(),
|
||||
];
|
||||
@@ -788,8 +788,8 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
$data = [
|
||||
'activity' => 1,
|
||||
'project' => 1,
|
||||
'begin' => ($dateTime->createDateTime('-8 hours'))->format('Y-m-d H:m:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:m:0'),
|
||||
'begin' => ($dateTime->createDateTime('-8 hours'))->format('Y-m-d H:i:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:i:0'),
|
||||
'description' => 'foo',
|
||||
];
|
||||
$json = json_encode($data);
|
||||
@@ -812,8 +812,8 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
$data = [
|
||||
'activity' => 1,
|
||||
'project' => 1,
|
||||
'begin' => ($dateTime->createDateTime('- 7 hours'))->format('Y-m-d\TH:m:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d\TH:m:0'),
|
||||
'begin' => ($dateTime->createDateTime('- 7 hours'))->format('Y-m-d\TH:i:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d\TH:i:0'),
|
||||
'description' => 'foo',
|
||||
'billable' => false,
|
||||
];
|
||||
@@ -853,8 +853,8 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
$data = [
|
||||
'activity' => 1,
|
||||
'project' => 1,
|
||||
'begin' => (new \DateTime('- 7 hours'))->format('Y-m-d\TH:m:s'),
|
||||
'end' => (new \DateTime())->format('Y-m-d\TH:m:s'),
|
||||
'begin' => (new \DateTime('- 7 hours'))->format('Y-m-d\TH:i:s'),
|
||||
'end' => (new \DateTime())->format('Y-m-d\TH:i:s'),
|
||||
'description' => 'foo',
|
||||
'exported' => true,
|
||||
];
|
||||
@@ -874,12 +874,14 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
$timesheets = $this->importFixtureForUser(User::ROLE_USER);
|
||||
$begin = new \DateTimeImmutable();
|
||||
$end = $begin->modify('-1 day');
|
||||
|
||||
$data = [
|
||||
'activity' => 10,
|
||||
'project' => 1,
|
||||
'begin' => (new \DateTime())->format('Y-m-d H:m'),
|
||||
'end' => (new \DateTime('-1 day'))->format('Y-m-d H:m'),
|
||||
'begin' => $begin->format('Y-m-d H:m'),
|
||||
'end' => $end->format('Y-m-d H:m'),
|
||||
'description' => 'foo',
|
||||
];
|
||||
$json = json_encode($data);
|
||||
@@ -1034,6 +1036,7 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
self::assertIsString($content);
|
||||
$result = json_decode($content, true);
|
||||
|
||||
self::assertIsArray($result);
|
||||
self::assertEquals(3, \count($result));
|
||||
foreach ($result as $timesheet) {
|
||||
self::assertIsArray($timesheet);
|
||||
@@ -1284,7 +1287,9 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
self::assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
||||
self::assertEquals('foo', $result['description']);
|
||||
self::assertEquals([['name' => 'sdfsdf', 'value' => 'nnnnn'], ['name' => '1234567890', 'value' => '1234567890']], $result['metaFields']);
|
||||
self::assertIsArray($result['metaFields']);
|
||||
self::assertEquals(['name' => 'sdfsdf', 'value' => 'nnnnn'], $result['metaFields'][0]);
|
||||
self::assertEquals(['name' => '1234567890', 'value' => '1234567890'], $result['metaFields'][1]);
|
||||
self::assertEquals(['another', 'testing', 'bar'], $result['tags']);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
@@ -1332,8 +1337,8 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
$data = [
|
||||
'activity' => 1,
|
||||
'project' => 1,
|
||||
'begin' => ($dateTime->createDateTime('- 8 hours'))->format('Y-m-d H:m:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:m:0'),
|
||||
'begin' => ($dateTime->createDateTime('- 8 hours'))->format('Y-m-d H:i:0'),
|
||||
'end' => ($dateTime->createDateTime())->format('Y-m-d H:i:0'),
|
||||
'description' => 'foo',
|
||||
'fixedRate' => 2016,
|
||||
'hourlyRate' => 127
|
||||
@@ -1488,7 +1493,16 @@ class TimesheetControllerTest extends APIControllerBaseTestCase
|
||||
|
||||
self::assertIsArray($result);
|
||||
self::assertApiResponseTypeStructure('TimesheetEntity', $result);
|
||||
self::assertEquals(['name' => 'metatestmock', 'value' => 'another,testing,bar'], $result['metaFields'][0]);
|
||||
self::assertIsArray($result['metaFields']);
|
||||
$found = false;
|
||||
foreach ($result['metaFields'] as $field) {
|
||||
self::assertIsArray($field);
|
||||
if ($field['name'] === 'metatestmock') {
|
||||
self::assertEquals(['name' => 'metatestmock', 'value' => 'another,testing,bar'], $field);
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
self::assertTrue($found);
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
/** @var Timesheet $timesheet */
|
||||
|
||||
@@ -327,7 +327,6 @@ class UserControllerTest extends APIControllerBaseTestCase
|
||||
'username' => '1', // not existing in form
|
||||
'email' => '',
|
||||
'plainPassword' => '123456', // not existing in form
|
||||
'plainApiToken' => '123456', // not existing in form
|
||||
'language' => 'xx',
|
||||
'timezone' => 'XXX/YYY',
|
||||
'roles' => [
|
||||
|
||||
Reference in New Issue
Block a user