feat(service): add api service
This commit is contained in:
parent
e929853bb2
commit
e1375f1bf5
@ -4,7 +4,7 @@
|
|||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.2",
|
"php": ">=8.3",
|
||||||
"ext-ctype": "*",
|
"ext-ctype": "*",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
"doctrine/dbal": "^3",
|
"doctrine/dbal": "^3",
|
||||||
|
10
config/services_test.yaml
Normal file
10
config/services_test.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
services:
|
||||||
|
App\Service\Remote\BriloApiUsers:
|
||||||
|
public: true
|
||||||
|
autowire: true
|
||||||
|
App\Service\Remote\BriloApiComments:
|
||||||
|
public: true
|
||||||
|
autowire: true
|
||||||
|
App\Service\Remote\BriloApiPosts:
|
||||||
|
public: true
|
||||||
|
autowire: true
|
@ -18,8 +18,11 @@
|
|||||||
</php>
|
</php>
|
||||||
|
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="Project Test Suite">
|
<testsuite name="unit">
|
||||||
<directory>tests</directory>
|
<directory>tests/Unit</directory>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="integration">
|
||||||
|
<directory>tests/Integration</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@ namespace App\Entity\Remote\Brilo\Users;
|
|||||||
readonly class AddressGeo
|
readonly class AddressGeo
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public float $lat,
|
public string $lat,
|
||||||
public float $lng,
|
public string $lng,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
src/Service/Remote/BriloApiComments.php
Normal file
35
src/Service/Remote/BriloApiComments.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service\Remote;
|
||||||
|
|
||||||
|
use App\Entity\Remote\Brilo\Comments\Comment;
|
||||||
|
use App\Entity\Remote\Brilo\Posts\Post;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
class BriloApiComments
|
||||||
|
{
|
||||||
|
use BriloApiFetchTrait;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected readonly HttpClientInterface $httpClient,
|
||||||
|
protected readonly LoggerInterface $logger,
|
||||||
|
protected readonly SerializerInterface $serializer,
|
||||||
|
protected readonly int $retryCount = 3,
|
||||||
|
protected readonly int $sleepTimeS = 1,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all comments from the Brilo API and return them as an array of Comment entities.
|
||||||
|
* @return Comment[]
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function getComments(): array
|
||||||
|
{
|
||||||
|
return $this->fetchApiResponse('https://jsonplaceholder.typicode.com/comments', Comment::class);
|
||||||
|
}
|
||||||
|
}
|
54
src/Service/Remote/BriloApiFetchTrait.php
Normal file
54
src/Service/Remote/BriloApiFetchTrait.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service\Remote;
|
||||||
|
|
||||||
|
use App\Entity\Remote\Brilo\Users\User;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\HttpClient\Exception\TransportException;
|
||||||
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
trait BriloApiFetchTrait
|
||||||
|
{
|
||||||
|
use RetryingClientTrait;
|
||||||
|
|
||||||
|
protected readonly int $retryCount;
|
||||||
|
protected readonly int $sleepTimeS;
|
||||||
|
protected readonly LoggerInterface $logger;
|
||||||
|
protected readonly HttpClientInterface $httpClient;
|
||||||
|
protected readonly SerializerInterface $serializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of fetch logic. Handles retrying and logging of failed requests.
|
||||||
|
* Returns deserialized data.
|
||||||
|
* @template T
|
||||||
|
*
|
||||||
|
* @param string $uri Uri of the API endpoint.
|
||||||
|
* @param class-string<T> $class Class name of the entity to deserialize data into.
|
||||||
|
*
|
||||||
|
* @return T[]
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
protected function fetchApiResponse(string $uri, string $class): array
|
||||||
|
{
|
||||||
|
return $this->retryingFailRequest($this->retryCount, $this->sleepTimeS, [TransportException::class, BriloRemoteApiException::class], $this->logger, function () use ($uri, $class) {
|
||||||
|
$this->logger->debug('Trying to download brilo users');
|
||||||
|
|
||||||
|
$response = $this->httpClient->request('GET', $uri);
|
||||||
|
|
||||||
|
if ($response->getStatusCode() != 200) {
|
||||||
|
$this->logger->error("Can't download brilo users. HTTP status code: " . $response->getStatusCode());
|
||||||
|
throw new BriloRemoteApiException("Can't download brilo users. HTTP status code: " . $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->debug('Brilo users downloaded');
|
||||||
|
|
||||||
|
$this->logger->debug('Deserialize brilo users');
|
||||||
|
|
||||||
|
// extra attributes muzeme safe ignorovat
|
||||||
|
return $this->serializer->deserialize($response->getContent(), $class . '[]', 'json');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
10
src/Service/Remote/BriloApiPosts.php
Normal file
10
src/Service/Remote/BriloApiPosts.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service\Remote;
|
||||||
|
|
||||||
|
class BriloApiPosts
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
35
src/Service/Remote/BriloApiUsers.php
Normal file
35
src/Service/Remote/BriloApiUsers.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service\Remote;
|
||||||
|
|
||||||
|
use App\Entity\Remote\Brilo\Users\User;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\HttpClient\Exception\TransportException;
|
||||||
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
final class BriloApiUsers
|
||||||
|
{
|
||||||
|
use BriloApiFetchTrait;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected readonly HttpClientInterface $httpClient,
|
||||||
|
protected readonly LoggerInterface $logger,
|
||||||
|
protected readonly SerializerInterface $serializer,
|
||||||
|
protected readonly int $retryCount = 3,
|
||||||
|
protected readonly int $sleepTimeS = 1,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return User[]
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function getUsers(): array
|
||||||
|
{
|
||||||
|
return $this->fetchApiResponse('https://jsonplaceholder.typicode.com/users', User::class);
|
||||||
|
}
|
||||||
|
}
|
12
src/Service/Remote/BriloRemoteApiException.php
Normal file
12
src/Service/Remote/BriloRemoteApiException.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service\Remote;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote API exception (in case return code from API endpoint is not 200 - OK)
|
||||||
|
*/
|
||||||
|
class BriloRemoteApiException extends \RuntimeException
|
||||||
|
{
|
||||||
|
}
|
58
src/Service/Remote/RetryingClientTrait.php
Normal file
58
src/Service/Remote/RetryingClientTrait.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service\Remote;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
trait RetryingClientTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Better to retry failed requests (in case of non-destructive operations) than throwing immediately.
|
||||||
|
*
|
||||||
|
* @param int $count
|
||||||
|
* @param float $sleep
|
||||||
|
* @param array<string> $catchableExceptions
|
||||||
|
* @param LoggerInterface $logger
|
||||||
|
* @param callable $callback
|
||||||
|
* @return mixed
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function retryingFailRequest(
|
||||||
|
int $count,
|
||||||
|
float $sleep,
|
||||||
|
array $catchableExceptions,
|
||||||
|
LoggerInterface $logger,
|
||||||
|
callable $callback
|
||||||
|
): mixed {
|
||||||
|
for ($i = 0;; $i++) {
|
||||||
|
try {
|
||||||
|
return $callback();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
foreach ($catchableExceptions as $exceptionClass) {
|
||||||
|
if ($e instanceof $exceptionClass) {
|
||||||
|
$logger->error("transport: fail request retrying... got catchable exception", [
|
||||||
|
'exception' => $e,
|
||||||
|
'try' => $i
|
||||||
|
]);
|
||||||
|
|
||||||
|
usleep((int) ($sleep * 1_000_000));
|
||||||
|
|
||||||
|
if ($i == $count) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// phpstan fail
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
21
tests/Common/LoggerTrait.php
Normal file
21
tests/Common/LoggerTrait.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Common;
|
||||||
|
|
||||||
|
use Monolog\Handler\TestHandler;
|
||||||
|
use Monolog\Logger;
|
||||||
|
|
||||||
|
trait LoggerTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return Logger
|
||||||
|
*/
|
||||||
|
protected function getLogger(): Logger
|
||||||
|
{
|
||||||
|
$logger = new Logger('test');
|
||||||
|
$logger->pushHandler(new TestHandler());
|
||||||
|
return $logger;
|
||||||
|
}
|
||||||
|
}
|
28
tests/Integration/Service/Remote/BlogApiUsersTest.php
Normal file
28
tests/Integration/Service/Remote/BlogApiUsersTest.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Integration\Service\Remote;
|
||||||
|
|
||||||
|
use App\Service\Remote\BriloApiUsers;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
class BlogApiUsersTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::bootKernel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBlogApiUsersAreRetrieved(): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var BriloApiUsers $apiUsers
|
||||||
|
*/
|
||||||
|
$apiUsers = static::getContainer()->get(BriloApiUsers::class);
|
||||||
|
$users = $apiUsers->getUsers();
|
||||||
|
$this->assertIsArray($users);
|
||||||
|
$this->assertGreaterThan(0, count($users));
|
||||||
|
// Tady bych dal jeste check na zaznam ktery vim ze vzdy existuje
|
||||||
|
}
|
||||||
|
}
|
28
tests/Integration/Service/Remote/BriloApiCommentsTest.php
Normal file
28
tests/Integration/Service/Remote/BriloApiCommentsTest.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Integration\Service\Remote;
|
||||||
|
|
||||||
|
use App\Service\Remote\BriloApiComments;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
class BriloApiCommentsTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::bootKernel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBlogApiUsersAreRetrieved(): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var BriloApiComments $api
|
||||||
|
*/
|
||||||
|
$api = static::getContainer()->get(BriloApiComments::class);
|
||||||
|
$users = $api->getComments();
|
||||||
|
$this->assertIsArray($users);
|
||||||
|
$this->assertGreaterThan(0, count($users));
|
||||||
|
// Tady bych dal jeste check na zaznam ktery vim ze vzdy existuje
|
||||||
|
}
|
||||||
|
}
|
28
tests/Integration/Service/Remote/BriloApiPostsTest.php
Normal file
28
tests/Integration/Service/Remote/BriloApiPostsTest.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Integration\Service\Remote;
|
||||||
|
|
||||||
|
use App\Service\Remote\BriloApiPosts;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
class BriloApiPostsTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::bootKernel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBlogApiUsersAreRetrieved(): void
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var BriloApiPosts $api
|
||||||
|
*/
|
||||||
|
$api = static::getContainer()->get(BriloApiPosts::class);
|
||||||
|
$users = $api->getPosts();
|
||||||
|
$this->assertIsArray($users);
|
||||||
|
$this->assertGreaterThan(0, count($users));
|
||||||
|
// Tady bych dal jeste check na zaznam ktery vim ze vzdy existuje
|
||||||
|
}
|
||||||
|
}
|
10
tests/Unit/Service/Remote/BlogApiPostsTest.php
Normal file
10
tests/Unit/Service/Remote/BlogApiPostsTest.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Unit\Service\Remote;
|
||||||
|
|
||||||
|
class BlogApiPostsTest
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
98
tests/Unit/Service/Remote/BriloApiCommentsTest.php
Normal file
98
tests/Unit/Service/Remote/BriloApiCommentsTest.php
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Unit\Service\Remote;
|
||||||
|
|
||||||
|
use App\Entity\Remote\Brilo\Comments\Comment;
|
||||||
|
use App\Entity\Remote\Brilo\Posts\Post;
|
||||||
|
use App\Service\Remote\BriloApiComments;
|
||||||
|
use App\Service\Remote\BriloApiPosts;
|
||||||
|
use App\Service\Remote\BriloRemoteApiException;
|
||||||
|
use App\Tests\Common\LoggerTrait;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpClient\MockHttpClient;
|
||||||
|
use Symfony\Component\HttpClient\Response\JsonMockResponse;
|
||||||
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
|
|
||||||
|
class BriloApiCommentsTest extends TestCase
|
||||||
|
{
|
||||||
|
use LoggerTrait;
|
||||||
|
|
||||||
|
private const array API_COMMENTS_LIST = [
|
||||||
|
[
|
||||||
|
"postId" => 1,
|
||||||
|
"id" => 1,
|
||||||
|
"name" => "id labore ex et quam laborum",
|
||||||
|
"email" => "Eliseo@gardner.biz",
|
||||||
|
"body" => "laudantium enim quasi est quidem magnam voluptate ipsam eos
|
||||||
|
tempora quo necessitatibus
|
||||||
|
dolor quam autem quasi
|
||||||
|
reiciendis et nam sapiente accusantium"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"postId" => 1,
|
||||||
|
"id" => 2,
|
||||||
|
"name" => "quo vero reiciendis velit similique earum",
|
||||||
|
"email" => "Jayne_Kuhic@sydney.com",
|
||||||
|
"body" => "est natus enim nihil est dolore omnis voluptatem numquam
|
||||||
|
et omnis occaecati quod ullam at
|
||||||
|
voluptatem error expedita pariatur
|
||||||
|
nihil sint nostrum voluptatem reiciendis et"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
public function testCommentsListSuccess(): void
|
||||||
|
{
|
||||||
|
$testCommentsList = $this->buildTestCommentsList();
|
||||||
|
$serializer = $this->createMock(SerializerInterface::class);
|
||||||
|
$serializer
|
||||||
|
->expects($this->once())
|
||||||
|
->method('deserialize')
|
||||||
|
->with(
|
||||||
|
json_encode(self::API_COMMENTS_LIST),
|
||||||
|
Comment::class . '[]',
|
||||||
|
'json'
|
||||||
|
)->willReturn($testCommentsList);
|
||||||
|
|
||||||
|
$response = new JsonMockResponse(self::API_COMMENTS_LIST);
|
||||||
|
$mockedClient = new MockHttpClient($response);
|
||||||
|
$briloApiPosts = new BriloApiComments($mockedClient, $this->getLogger(), $serializer);
|
||||||
|
$this->assertEquals($testCommentsList, $briloApiPosts->getComments());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidHttpCodeReturned(): void
|
||||||
|
{
|
||||||
|
$serializer = $this->createMock(SerializerInterface::class);
|
||||||
|
$serializer
|
||||||
|
->expects($this->never())
|
||||||
|
->method('deserialize');
|
||||||
|
|
||||||
|
$response = new JsonMockResponse(self::API_COMMENTS_LIST, [
|
||||||
|
'http_code' => 401,
|
||||||
|
]);
|
||||||
|
$mockedClient = new MockHttpClient(function () use ($response) {
|
||||||
|
return $response;
|
||||||
|
});
|
||||||
|
$this->expectException(BriloRemoteApiException::class);
|
||||||
|
$briloApiPosts = new BriloApiComments($mockedClient, $this->getLogger(), $serializer);
|
||||||
|
$briloApiPosts->getComments();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Comment[]
|
||||||
|
*/
|
||||||
|
public function buildTestCommentsList(): array
|
||||||
|
{
|
||||||
|
// Convert self::API_POSTS_LIST to Post[] and return it
|
||||||
|
return array_map(function ($postData) {
|
||||||
|
return new Comment(
|
||||||
|
$postData['id'],
|
||||||
|
$postData['postId'],
|
||||||
|
$postData['name'],
|
||||||
|
$postData['email'],
|
||||||
|
$postData['body']
|
||||||
|
);
|
||||||
|
}, self::API_COMMENTS_LIST);
|
||||||
|
}
|
||||||
|
}
|
142
tests/Unit/Service/Remote/BriloApiUsersTest.php
Normal file
142
tests/Unit/Service/Remote/BriloApiUsersTest.php
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Unit\Service\Remote;
|
||||||
|
|
||||||
|
use App\Entity\Remote\Brilo\Users\Address;
|
||||||
|
use App\Entity\Remote\Brilo\Users\AddressGeo;
|
||||||
|
use App\Entity\Remote\Brilo\Users\Company;
|
||||||
|
use App\Entity\Remote\Brilo\Users\User;
|
||||||
|
use App\Service\Remote\BriloApiUsers;
|
||||||
|
use App\Service\Remote\BriloRemoteApiException;
|
||||||
|
use App\Tests\Common\LoggerTrait;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\HttpClient\MockHttpClient;
|
||||||
|
use Symfony\Component\HttpClient\Response\JsonMockResponse;
|
||||||
|
use Symfony\Component\Serializer\SerializerInterface;
|
||||||
|
|
||||||
|
class BriloApiUsersTest extends TestCase
|
||||||
|
{
|
||||||
|
use LoggerTrait;
|
||||||
|
|
||||||
|
private const array API_USER_LIST = [
|
||||||
|
[
|
||||||
|
"id" => 0,
|
||||||
|
"name" => "Leanne Graham",
|
||||||
|
"username" => "Bret",
|
||||||
|
"email" => "Sincere@april.biz",
|
||||||
|
"address" => [
|
||||||
|
"street" => "Kulas Light",
|
||||||
|
"suite" => "Apt. 555",
|
||||||
|
"city" => "Gwenborough",
|
||||||
|
"zipcode" => "92997-3874",
|
||||||
|
"geo" => [
|
||||||
|
"lat" => "-38.3159",
|
||||||
|
"lng" => "80.1496"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"phone" => "0-770-736-8031 x56442",
|
||||||
|
"website" => "hildegard.org",
|
||||||
|
"company" => [
|
||||||
|
"name" => "Romaguera-Crona",
|
||||||
|
"catchPhrase" => "Multi-layered client-server neural-net",
|
||||||
|
"bs" => "harness real-time e-markets"
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"id" => 1,
|
||||||
|
"name" => "Ervin Howell",
|
||||||
|
"username" => "Antonette",
|
||||||
|
"email" => "Shanna@melissa.tv",
|
||||||
|
"address" => [
|
||||||
|
"street" => "Victor Plains",
|
||||||
|
"suite" => "Suite 878",
|
||||||
|
"city" => "Wisokyburgh",
|
||||||
|
"zipcode" => "90565-7771",
|
||||||
|
"geo" => [
|
||||||
|
"lat" => "-44.9509",
|
||||||
|
"lng" => "-35.4618"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"phone" => "009-692-6593 x09125",
|
||||||
|
"website" => "anastasia.net",
|
||||||
|
"company" => [
|
||||||
|
"name" => "Deckow-Crist",
|
||||||
|
"catchPhrase" => "Proactive didactic contingency",
|
||||||
|
"bs" => "synergize scalable supply-chains"
|
||||||
|
],
|
||||||
|
"nonExistentKey" => "value"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
public function testUserListSuccess(): void
|
||||||
|
{
|
||||||
|
$testUserList = $this->buildTestUserList();
|
||||||
|
$serializer = $this->createMock(SerializerInterface::class);
|
||||||
|
$serializer
|
||||||
|
->expects($this->once())
|
||||||
|
->method('deserialize')
|
||||||
|
->with(
|
||||||
|
json_encode(self::API_USER_LIST),
|
||||||
|
User::class . '[]',
|
||||||
|
'json'
|
||||||
|
)->willReturn($testUserList);
|
||||||
|
|
||||||
|
$response = new JsonMockResponse(self::API_USER_LIST);
|
||||||
|
$mockedClient = new MockHttpClient($response);
|
||||||
|
$briloApiUsers = new BriloApiUsers($mockedClient, $this->getLogger(), $serializer);
|
||||||
|
$this->assertEquals($testUserList, $briloApiUsers->getUsers());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInvalidHttpCodeReturned(): void
|
||||||
|
{
|
||||||
|
$serializer = $this->createMock(SerializerInterface::class);
|
||||||
|
$serializer
|
||||||
|
->expects($this->never())
|
||||||
|
->method('deserialize');
|
||||||
|
|
||||||
|
$response = new JsonMockResponse(self::API_USER_LIST, [
|
||||||
|
'http_code' => 401,
|
||||||
|
]);
|
||||||
|
$mockedClient = new MockHttpClient(function () use ($response) {
|
||||||
|
return $response;
|
||||||
|
});
|
||||||
|
$this->expectException(BriloRemoteApiException::class);
|
||||||
|
$briloApiUsers = new BriloApiUsers($mockedClient, $this->getLogger(), $serializer);
|
||||||
|
$briloApiUsers->getUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return User[]
|
||||||
|
*/
|
||||||
|
private function buildTestUserList(): array
|
||||||
|
{
|
||||||
|
// convert self::API_USER_LIST to User[]
|
||||||
|
return array_map(function ($userData) {
|
||||||
|
return new User(
|
||||||
|
$userData['id'],
|
||||||
|
$userData['name'],
|
||||||
|
$userData['username'],
|
||||||
|
$userData['email'],
|
||||||
|
$userData['phone'],
|
||||||
|
$userData['website'],
|
||||||
|
new Address(
|
||||||
|
$userData['address']['street'],
|
||||||
|
$userData['address']['suite'],
|
||||||
|
$userData['address']['city'],
|
||||||
|
$userData['address']['zipcode'],
|
||||||
|
new AddressGeo(
|
||||||
|
$userData['address']['geo']['lat'],
|
||||||
|
$userData['address']['geo']['lng']
|
||||||
|
)
|
||||||
|
),
|
||||||
|
new Company(
|
||||||
|
$userData['company']['name'],
|
||||||
|
$userData['company']['catchPhrase'],
|
||||||
|
$userData['company']['bs']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, self::API_USER_LIST);
|
||||||
|
}
|
||||||
|
}
|
62
tests/Unit/Service/Remote/RetryingClientTraitTest.php
Normal file
62
tests/Unit/Service/Remote/RetryingClientTraitTest.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Unit\Service\Remote;
|
||||||
|
|
||||||
|
use App\Service\Remote\RetryingClientTrait;
|
||||||
|
use App\Service\Remote\RetryingFailClientTrait;
|
||||||
|
use App\Tests\Common\LoggerTrait;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class RetryingClientTraitTest extends TestCase
|
||||||
|
{
|
||||||
|
use LoggerTrait;
|
||||||
|
|
||||||
|
private int $callCount = 0;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp(); // TODO: Change the autogenerated stub
|
||||||
|
$this->callCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSuccess(): void
|
||||||
|
{
|
||||||
|
$trait = new class {
|
||||||
|
use RetryingClientTrait {
|
||||||
|
retryingFailRequest as public; // make the method public
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$result = $trait->retryingFailRequest(2, 0, [\RuntimeException::class], $this->getLogger(), function () {
|
||||||
|
$this->callCount = $this->callCount + 1;
|
||||||
|
return 'foo';
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->assertEquals(1, $this->callCount);
|
||||||
|
$this->assertEquals('foo', $result);
|
||||||
|
}
|
||||||
|
public function testRetyingFail(): void
|
||||||
|
{
|
||||||
|
$trait = new class {
|
||||||
|
use RetryingClientTrait {
|
||||||
|
retryingFailRequest as public; // make the method public
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
$trait->retryingFailRequest(2, 0, [\RuntimeException::class], $this->getLogger(), function () {
|
||||||
|
$this->callCount = $this->callCount + 1;
|
||||||
|
throw new \RuntimeException("test");
|
||||||
|
});
|
||||||
|
} catch (\RuntimeException) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEquals(3, $this->callCount);
|
||||||
|
}
|
||||||
|
// extra attributes muzeme safe ignorovat
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user