2020-04-08 14:50:15 +02:00
|
|
|
<?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;
|
|
|
|
|
2022-12-31 21:19:55 +01:00
|
|
|
use App\Entity\ActivityRate;
|
|
|
|
use App\Entity\CustomerRate;
|
|
|
|
use App\Entity\ProjectRate;
|
2021-03-08 16:06:22 +01:00
|
|
|
use App\Entity\RateInterface;
|
2020-04-08 14:50:15 +02:00
|
|
|
use App\Entity\User;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @group integration
|
|
|
|
*/
|
|
|
|
trait RateControllerTestTrait
|
|
|
|
{
|
2025-02-09 00:16:03 +01:00
|
|
|
abstract protected function getRateUrl(?int $id = 1, ?int $rateId = null): string;
|
2020-04-19 14:37:14 +02:00
|
|
|
|
2021-03-08 16:06:22 +01:00
|
|
|
abstract protected function getRateUrlByRate(RateInterface $rate, bool $isCollection): string;
|
|
|
|
|
2020-04-19 14:37:14 +02:00
|
|
|
/**
|
2021-03-08 16:06:22 +01:00
|
|
|
* @return RateInterface[]
|
2020-04-19 14:37:14 +02:00
|
|
|
*/
|
2024-03-10 15:35:59 +01:00
|
|
|
abstract protected function importTestRates(string|int $id): array;
|
2020-04-08 14:50:15 +02:00
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testAddRateMissingEntityAction(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$data = [
|
|
|
|
'user' => 1,
|
|
|
|
'rate' => 12.34,
|
|
|
|
'internal_rate' => 6.66,
|
|
|
|
'is_fixed' => false
|
|
|
|
];
|
2022-12-31 21:19:55 +01:00
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
|
|
|
$this->assertEntityNotFoundForPost($client, $this->getRateUrl(99), $data);
|
2020-04-08 14:50:15 +02:00
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testAddRateMissingUserAction(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
|
|
|
$data = [
|
|
|
|
'user' => 33,
|
|
|
|
'rate' => 12.34,
|
|
|
|
'internal_rate' => 6.66,
|
|
|
|
'is_fixed' => false
|
|
|
|
];
|
|
|
|
$this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data));
|
|
|
|
|
|
|
|
$response = $client->getResponse();
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertEquals(400, $response->getStatusCode());
|
2020-04-08 14:50:15 +02:00
|
|
|
$this->assertApiCallValidationError($response, ['user']);
|
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testAddRateActionWithInvalidUser(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
|
|
|
$data = [
|
|
|
|
'user' => null,
|
|
|
|
'rate' => 12.34,
|
|
|
|
'internal_rate' => 6.66,
|
|
|
|
'is_fixed' => false
|
|
|
|
];
|
|
|
|
$this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data));
|
|
|
|
$response = $client->getResponse();
|
2022-12-31 21:19:55 +01:00
|
|
|
$this->assertApiResponseAccessDenied($response, 'Access denied.');
|
2020-04-08 14:50:15 +02:00
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testAddRateAction(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
|
|
|
$data = [
|
|
|
|
'user' => null,
|
|
|
|
'rate' => 12.34,
|
|
|
|
'internal_rate' => 6.66,
|
|
|
|
'is_fixed' => false
|
|
|
|
];
|
|
|
|
$this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data));
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertTrue($client->getResponse()->isSuccessful());
|
2020-04-08 14:50:15 +02:00
|
|
|
|
2024-03-10 15:35:59 +01:00
|
|
|
$content = $client->getResponse()->getContent();
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsString($content);
|
2024-03-10 15:35:59 +01:00
|
|
|
$result = json_decode($content, true);
|
|
|
|
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsArray($result);
|
2020-04-08 14:50:15 +02:00
|
|
|
$this->assertRateStructure($result, null);
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertNotEmpty($result['id']);
|
|
|
|
self::assertEquals(12.34, $result['rate']);
|
|
|
|
self::assertEquals(6.66, $result['internalRate']);
|
|
|
|
self::assertFalse($result['isFixed']);
|
2020-04-08 14:50:15 +02:00
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testAddFixedRateAction(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
|
|
|
$data = [
|
|
|
|
'user' => 1,
|
|
|
|
'rate' => 12.34,
|
|
|
|
'internal_rate' => 6.66,
|
|
|
|
'is_fixed' => true
|
|
|
|
];
|
|
|
|
$this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data));
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertTrue($client->getResponse()->isSuccessful());
|
2020-04-08 14:50:15 +02:00
|
|
|
|
2024-03-10 15:35:59 +01:00
|
|
|
$content = $client->getResponse()->getContent();
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsString($content);
|
2024-03-10 15:35:59 +01:00
|
|
|
$result = json_decode($content, true);
|
|
|
|
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsArray($result);
|
2020-04-08 14:50:15 +02:00
|
|
|
$this->assertRateStructure($result, 1);
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertNotEmpty($result['id']);
|
|
|
|
self::assertEquals(12.34, $result['rate']);
|
|
|
|
self::assertEquals(6.66, $result['internalRate']);
|
|
|
|
self::assertTrue($result['isFixed']);
|
2020-04-08 14:50:15 +02:00
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testGetRatesEmptyResult(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
|
|
|
$this->request($client, $this->getRateUrl(1));
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertTrue($client->getResponse()->isSuccessful());
|
2020-04-08 14:50:15 +02:00
|
|
|
|
2024-03-10 15:35:59 +01:00
|
|
|
$content = $client->getResponse()->getContent();
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsString($content);
|
2024-03-10 15:35:59 +01:00
|
|
|
$result = json_decode($content, true);
|
|
|
|
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsArray($result);
|
|
|
|
self::assertEmpty($result);
|
2020-04-08 14:50:15 +02:00
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testGetRates(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
|
|
|
$expectedRates = $this->importTestRates(1);
|
|
|
|
|
|
|
|
$this->request($client, $this->getRateUrl(1));
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertTrue($client->getResponse()->isSuccessful());
|
2020-04-08 14:50:15 +02:00
|
|
|
|
2024-03-10 15:35:59 +01:00
|
|
|
$content = $client->getResponse()->getContent();
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsString($content);
|
2024-03-10 15:35:59 +01:00
|
|
|
$result = json_decode($content, true);
|
|
|
|
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsArray($result);
|
|
|
|
self::assertNotEmpty($result);
|
|
|
|
self::assertEquals(\count($expectedRates), \count($result));
|
2020-04-08 14:50:15 +02:00
|
|
|
|
|
|
|
foreach ($result as $rate) {
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsArray($rate);
|
2024-11-21 22:44:49 +01:00
|
|
|
if ($rate['user'] === null) {
|
|
|
|
$this->assertRateStructure($rate);
|
|
|
|
} else {
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsArray($rate['user']);
|
2024-11-21 22:44:49 +01:00
|
|
|
$this->assertRateStructure($rate, $rate['user']['id']);
|
|
|
|
}
|
2020-04-08 14:50:15 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testGetRatesEntityNotFound(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$this->assertEntityNotFound(User::ROLE_ADMIN, $this->getRateUrl(99));
|
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testGetRatesIsSecured(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$this->assertUrlIsSecuredForRole(User::ROLE_USER, $this->getRateUrl(1));
|
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testDeleteRate(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
|
|
|
$expectedRates = $this->importTestRates(1);
|
|
|
|
|
2021-03-08 16:06:22 +01:00
|
|
|
$this->request($client, $this->getRateUrlByRate($expectedRates[0], false), 'DELETE');
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertTrue($client->getResponse()->isSuccessful());
|
|
|
|
self::assertEmpty($client->getResponse()->getContent());
|
2020-04-08 14:50:15 +02:00
|
|
|
|
|
|
|
// fetch rates to validate that one was removed
|
2021-03-08 16:06:22 +01:00
|
|
|
$this->request($client, $this->getRateUrlByRate($expectedRates[0], true));
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertTrue($client->getResponse()->isSuccessful());
|
2020-04-08 14:50:15 +02:00
|
|
|
|
2024-03-10 15:35:59 +01:00
|
|
|
$content = $client->getResponse()->getContent();
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsString($content);
|
2024-03-10 15:35:59 +01:00
|
|
|
$result = json_decode($content, true);
|
|
|
|
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertIsArray($result);
|
|
|
|
self::assertEquals(\count($expectedRates) - 1, \count($result));
|
2020-04-08 14:50:15 +02:00
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testDeleteRateEntityNotFound(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
2022-12-31 21:19:55 +01:00
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
|
|
|
$this->assertNotFoundForDelete($client, $this->getRateUrl(99, 1));
|
2020-04-08 14:50:15 +02:00
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testDeleteRateRateNotFound(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
2022-12-31 21:19:55 +01:00
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
|
|
|
$this->assertNotFoundForDelete($client, $this->getRateUrl(1, 99));
|
2020-04-08 14:50:15 +02:00
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testDeleteRateWithInvalidAssignment(): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
|
|
|
|
$this->importTestRates(1);
|
|
|
|
$this->importTestRates(2);
|
|
|
|
|
|
|
|
$this->assertNotFoundForDelete($client, $this->getRateUrl(2, 1));
|
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function testDeleteNotAllowed(): void
|
2020-07-17 20:30:16 +02:00
|
|
|
{
|
|
|
|
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
2022-12-31 21:19:55 +01:00
|
|
|
$rates = $this->importTestRates(1);
|
|
|
|
|
|
|
|
/** @var ActivityRate|ProjectRate|CustomerRate $rate */
|
|
|
|
$rate = $rates[0];
|
2020-07-17 20:30:16 +02:00
|
|
|
|
2022-12-31 21:19:55 +01:00
|
|
|
$this->request($client, $this->getRateUrl(1, $rate->getId()), 'DELETE');
|
2020-07-17 20:30:16 +02:00
|
|
|
$this->assertApiResponseAccessDenied($client->getResponse(), 'Access denied.');
|
|
|
|
}
|
|
|
|
|
2024-02-07 23:47:25 +01:00
|
|
|
public function assertRateStructure(array $result, $user = null): void
|
2020-04-08 14:50:15 +02:00
|
|
|
{
|
|
|
|
$expectedKeys = [
|
|
|
|
'id', 'rate', 'internalRate', 'isFixed', 'user'
|
|
|
|
];
|
|
|
|
|
|
|
|
$actual = array_keys($result);
|
|
|
|
sort($actual);
|
|
|
|
sort($expectedKeys);
|
|
|
|
|
2024-12-22 01:25:30 +01:00
|
|
|
self::assertEquals($expectedKeys, $actual, 'Rate structure does not match');
|
2020-04-08 14:50:15 +02:00
|
|
|
|
|
|
|
if (null !== $user) {
|
|
|
|
self::assertIsArray($result['user'], 'Rate user is not an array');
|
|
|
|
self::assertEquals($user, $result['user']['id'], 'Rate user does not match');
|
|
|
|
} else {
|
|
|
|
self::assertNull($result['user']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|