Olivier Massot 4 anni fa
parent
commit
24c167a53d

+ 10 - 2
ot_core/Classes/Domain/Repository/BaseApiRepository.php

@@ -32,8 +32,16 @@ abstract class BaseApiRepository implements LoggerAwareInterface
     /**
      * BaseApiRepository constructor.
      */
-    public function __construct() {
-        $this->apiService = GeneralUtility::makeInstance(OpentalentApiService::class);
+    public function __construct($apiService = null) {
+        $this->apiService = $apiService ?? GeneralUtility::makeInstance(OpentalentApiService::class);
+    }
+
+    /**
+     * [FOR TESTS ONLY]
+     * @param Client $client
+     */
+    protected function injectService(OpentalentApiService $apiService) {
+        $this->apiService = $apiService;
     }
 
     /**

+ 10 - 9
ot_core/Classes/Service/OpentalentApiService.php

@@ -27,12 +27,12 @@ class OpentalentApiService implements LoggerAwareInterface
 
     /**
      * @param ObjectManagerInterface $objectManager
-     * @param Client|null $client  [For tests only]
-     * @param ApplicationContext|null $context  [For tests only]
+     * @param object|null $client  [For tests only]
+     * @param object|null $context  [For tests only]
      */
     public function __construct(
-        ?Client $client = null,
-        ?ApplicationContext $context = null
+        ?object $client = null,
+        ?object $context = null
     ) {
         // Get the current context (prod, dev...)
         if ($context === null) {
@@ -52,7 +52,7 @@ class OpentalentApiService implements LoggerAwareInterface
      * [FOR TESTS ONLY]
      * @param Client $client
      */
-    protected function injectClient(Client $client) {
+    public function injectClient(Client $client) {
         $this->client = $client;
     }
 
@@ -140,17 +140,18 @@ class OpentalentApiService implements LoggerAwareInterface
         string $httpMethod,
         string $uri,
         array $params = []
-    ): ResponseInterface
+    ): ?ResponseInterface
     {
         $parsedUrl = parse_url($uri);
 
-        $params['_format'] = 'json';
-        array_unshift($params, $parsedUrl['query']);
-        $uri = $uri . '?' . http_build_query($params);
+        $uri = rtrim($uri, '/') . '?_format=json';
 
         if ($parsedUrl['query']) {
             $uri = $uri . '&' . $parsedUrl['query'];
         }
+        if ($params) {
+            $uri .= '&' . http_build_query($params);
+        }
 
         try {
             if ($this->context->isDevelopment()) {

+ 13 - 5
ot_core/Tests/Unit/Domain/Repository/AbstractApiRepositoryTestCase.php

@@ -5,7 +5,9 @@ namespace Opentalent\OtCore\Tests\Unit\Domain\Repository;
 use GuzzleHttp\Client;
 use Nimut\TestingFramework\TestCase\UnitTestCase;
 use Opentalent\OtCore\Domain\Repository\BaseApiRepository;
+use Opentalent\OtCore\Service\OpentalentApiService;
 use Opentalent\OtCore\Tests\Unit\Fixtures\ApiResponseFixtures;
+use Prophecy\Argument;
 use ReflectionClass;
 use TYPO3\CMS\Core\Core\ApplicationContext;
 
@@ -32,6 +34,8 @@ abstract class AbstractApiRepositoryTestCase extends UnitTestCase
      */
     protected $client;
 
+    protected $service;
+
     public function setUp() {
         // mock the application context
         $this->context = $this->prophesize(ApplicationContext::class);
@@ -40,29 +44,33 @@ abstract class AbstractApiRepositoryTestCase extends UnitTestCase
         $this->context->isTesting()->willReturn(true);
 
         $this->client = $this->prophesize(Client::class);
+        $this->service = new OpentalentApiService(
+            $this->client->reveal(),
+            $this->context->reveal()
+        );
 
         $repositoryClass = new ReflectionClass(static::TESTED_CLASS);
         foreach ($repositoryClass->getMethods() as $method) {
             $method->setAccessible(true);
         }
-        $this->repository = $repositoryClass->newInstanceArgs([$this->client->reveal(), $this->context->reveal()]);
+        $this->repository = $repositoryClass->newInstanceArgs([$this->service]);
 
         $this->fixture = new ApiResponseFixtures();
     }
 
-    protected function injectClientFor($uri) {
+    protected function injectClientFor($uri, $http_method='GET') {
         // mock the Guzzle client
         $willReturn = $this->fixture->get($uri);
         $client = $this->prophesize(Client::class);
-        $client->request(BaseApiRepository::HTTP_METHOD, $uri)
+        $client->request($http_method, $uri)
             ->shouldBeCalled()
             ->willReturn($willReturn);
 
-        $reflectionObject = new \ReflectionObject($this->repository);
+        $reflectionObject = new \ReflectionObject($this->service);
         $reflectionMethod = $reflectionObject->getMethod('injectClient');
         $reflectionMethod->setAccessible(true);
 
-        $reflectionMethod->invokeArgs($this->repository, [$client->reveal()]);
+        $reflectionMethod->invokeArgs($this->service, [$client->reveal()]);
     }
 
     protected function callMemberToObject(array $record) {

+ 2 - 110
ot_core/Tests/Unit/Domain/Repository/BaseApiRepositoryTest.php

@@ -13,10 +13,6 @@ use Opentalent\OtCore\Exception\ApiRequestException;
  */
 class ConcreteBaseApiRepository extends BaseApiRepository {
     protected function memberToObject(array $member) { return $member; }
-    public function injectClient($client) { parent::injectClient($client); }
-    public function getResponse($uri, $params = []): \Psr\Http\Message\ResponseInterface { return parent::getResponse($uri, $params); }
-    public function getBody($uri, $params = []): string { return parent::getBody($uri, $params); }
-    public function getJsonDecoded($uri, $params = []): array { return parent::getJsonDecoded($uri, $params); }
     public function getApiFirstRecord($params = [], $forceUri = null) { return parent::getApiFirstRecord($params, $forceUri); }
     public function getApiRecords($params = [], $forceUri = null): ApiPagedCollection { return parent::getApiRecords($params, $forceUri); }
 }
@@ -25,110 +21,6 @@ class BaseApiRepositoryTest extends AbstractApiRepositoryTestCase
 {
     const TESTED_CLASS = 'Opentalent\OtCore\Tests\Unit\Domain\Repository\ConcreteBaseApiRepository';
 
-    /**
-     * get should build a valid url, send a query and
-     * return a Guzzle response object
-     *
-     * @test
-     */
-    public function get() {
-
-        $base_uri = "https://api.opentalent.fr/api/public/organizations";
-        $params = ['itemsPerPage' => 10, 'foo' => 1];
-
-        // uri as it is supposed to be processed by the repo
-        $processed_uri = $base_uri . "?_format=json&itemsPerPage=10&foo=1";
-
-        $this->injectClientFor($processed_uri);
-        $actual = $this->repository->getResponse($base_uri, $params);
-
-        $this->assertEquals(200, $actual->getStatusCode());
-    }
-
-    /**
-     * get should build a valid url, send a query and
-     * return a Guzzle response object
-     *
-     * @test
-     */
-    public function getWithNoParams() {
-
-        $base_uri = "https://api.opentalent.fr/api/public/organizations";
-        $params = [];
-
-        // uri as it is supposed to be processed by the repo
-        $processed_uri = $base_uri . "?_format=json&itemsPerPage=8";
-
-        $this->injectClientFor($processed_uri);
-        $actual = $this->repository->getResponse($base_uri, $params);
-
-        $this->assertEquals(200, $actual->getStatusCode());
-    }
-
-    /**
-     * get should build a valid url, send a query and
-     * return a Guzzle response object
-     *
-     * @test
-     */
-    public function getInvalidUri()
-    {
-        $base_uri = "a very bad uri";
-        $params = [];
-        $processed_uri = $base_uri . "?_format=json&itemsPerPage=8";
-
-        $client = $this->prophesize(Client::class);
-        $client->request(BaseApiRepository::HTTP_METHOD, $processed_uri)
-            ->shouldBeCalled()
-            ->willThrow(new \GuzzleHttp\Exception\TransferException('error'));
-        $this->inject($this->repository, "client", $client->reveal());
-
-        try {
-            $this->repository->getResponse($base_uri, $params);
-            throw new \AssertionError("An ApiRequestException should have been thrown");
-        } catch (ApiRequestException $e) {
-            $this->assertEquals('error', $e->getMessage());
-        }
-    }
-
-    /**
-     * getBody should return the response body as a string
-     *
-     * @test
-     */
-    public function getBody() {
-
-        $base_uri = "https://api.opentalent.fr/api/public/organizations";
-        $params = ['itemsPerPage' => 10, 'foo' => 1];
-
-        // uri as it is supposed to be processed by the repo
-        $processed_uri = $base_uri . "?_format=json&itemsPerPage=10&foo=1";
-
-        $this->injectClientFor($processed_uri);
-        $actual = $this->repository->getBody($base_uri, $params);
-
-        $this->assertEquals('{"@context": "/api/contexts/PortailOrganization"}', $actual);
-    }
-
-    /**
-     * getBody should return the response body as an array
-     *
-     * @test
-     */
-    public function getJsonDecoded() {
-
-        $base_uri = "https://api.opentalent.fr/api/public/organizations";
-        $params = ['itemsPerPage' => 10, 'foo' => 1];
-
-        // uri as it is supposed to be processed by the repo
-        $processed_uri = $base_uri . "?_format=json&itemsPerPage=10&foo=1";
-
-        $this->injectClientFor($processed_uri);
-        $actual = $this->repository->getJsonDecoded($base_uri, $params);
-
-        $this->assertEquals(["@context" => "/api/contexts/PortailOrganization"], $actual);
-    }
-
     /**
      * getApiFirstRecord should return the first member of the api response
      * this member has been processed by the memberToObject method, which does nothing here
@@ -136,7 +28,7 @@ class BaseApiRepositoryTest extends AbstractApiRepositoryTestCase
      * @test
      */
     public function getApiFirstRecord() {
-        $base_uri = "https://api.opentalent.fr/api/public/organizations";
+        $base_uri = "api/public/organizations";
         $params = ['filter[where][id]' => 1];
         $processed_uri = $base_uri . "?_format=json&filter%5Bwhere%5D%5Bid%5D=1&page=1&totalItems=1&itemsPerPage=8";
         $this->injectClientFor($processed_uri);
@@ -153,7 +45,7 @@ class BaseApiRepositoryTest extends AbstractApiRepositoryTestCase
      * @test
      */
     public function getApiRecords() {
-        $base_uri = "https://api.opentalent.fr/api/public/organizations";
+        $base_uri = "api/public/organizations";
         $params = ['filter[where][id]' => 1];
         $processed_uri = $base_uri . "?_format=json&filter%5Bwhere%5D%5Bid%5D=1&itemsPerPage=8";
         $this->injectClientFor($processed_uri);

+ 2 - 2
ot_core/Tests/Unit/Domain/Repository/DonorRepositoryTest.php

@@ -18,7 +18,7 @@ class DonorRepositoryTest extends AbstractApiRepositoryTestCase
     public function findByOrganizationId() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/donors?_format=json&organizationId=1&page=1&itemsPerPage=8";
+        $expected_uri = "api/public/donors?_format=json&organizationId=1&page=1&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findByOrganizationId($organization_id);
@@ -34,7 +34,7 @@ class DonorRepositoryTest extends AbstractApiRepositoryTestCase
     public function findParentsByOrganizationId() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/donors?_format=json&organizationId=1&parent=1&page=1&itemsPerPage=8";
+        $expected_uri = "api/public/donors?_format=json&organizationId=1&parent=1&page=1&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findParentsByOrganizationId($organization_id);

+ 8 - 8
ot_core/Tests/Unit/Domain/Repository/EventRepositoryTest.php

@@ -17,7 +17,7 @@ class EventRepositoryTest extends AbstractApiRepositoryTestCase
     public function findById() {
         $event_id = 2;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/events?_format=json&filter%5Bwhere%5D%5Bid%5D=2&page=1&totalItems=1&itemsPerPage=8";
+        $expected_uri = "api/public/events?_format=json&filter%5Bwhere%5D%5Bid%5D=2&page=1&totalItems=1&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findById($event_id);
@@ -33,7 +33,7 @@ class EventRepositoryTest extends AbstractApiRepositoryTestCase
     public function findByOrganizationId() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&page=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&itemsPerPage=8";
+        $expected_uri = "api/public/events?_format=json&organizationId=1&page=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findByOrganizationId($organization_id);
@@ -49,7 +49,7 @@ class EventRepositoryTest extends AbstractApiRepositoryTestCase
     public function findByOrganizationIdWithParams() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&page=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC";
+        $expected_uri = "api/public/events?_format=json&organizationId=1&page=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findByOrganizationId(
@@ -71,7 +71,7 @@ class EventRepositoryTest extends AbstractApiRepositoryTestCase
     public function findParentsByOrganizationId() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&page=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&parent=1&itemsPerPage=8";
+        $expected_uri = "api/public/events?_format=json&organizationId=1&page=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&parent=1&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findParentsByOrganizationId($organization_id);
@@ -87,7 +87,7 @@ class EventRepositoryTest extends AbstractApiRepositoryTestCase
     public function findParentsByOrganizationIdWithParams() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&page=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&parent=1";
+        $expected_uri = "api/public/events?_format=json&organizationId=1&page=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&parent=1";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findParentsByOrganizationId(
@@ -109,7 +109,7 @@ class EventRepositoryTest extends AbstractApiRepositoryTestCase
     public function findChildrenByOrganizationId() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&children=1&itemsPerPage=8";
+        $expected_uri = "api/public/events?_format=json&organizationId=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&children=1&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findChildrenByOrganizationId($organization_id);
@@ -125,7 +125,7 @@ class EventRepositoryTest extends AbstractApiRepositoryTestCase
     public function findChildrenByOrganizationIdWithParams() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&children=1";
+        $expected_uri = "api/public/events?_format=json&organizationId=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&children=1";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findChildrenByOrganizationId(
@@ -147,7 +147,7 @@ class EventRepositoryTest extends AbstractApiRepositoryTestCase
     public function searchBy() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&filter%5Bwhere%5D%5Bid%5D=1&itemsPerPage=8";
+        $expected_uri = "api/public/events?_format=json&organizationId=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&filter%5Bwhere%5D%5Bid%5D=1&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->searchBy($organization_id, ['filter[where][id]' => 1]);

+ 1 - 17
ot_core/Tests/Unit/Domain/Repository/MemberRepositoryTest.php

@@ -18,29 +18,13 @@ class MemberRepositoryTest extends AbstractApiRepositoryTestCase
     public function findByOrganizationId() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/members?_format=json&filter%5Bwhere%5D%5BorganizationId%5D=1&itemsPerPage=200";
+        $expected_uri = "api/public/members?_format=json&filter%5Bwhere%5D%5BorganizationId%5D=1&itemsPerPage=200";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findByOrganizationId($organization_id);
         $this->assertEquals($organization_id, $actual->getMembers()[0]->getOrganizationId());
     }
 
-    /**
-     * findByOrganizationId should return an ApiPagedCollection object containing
-     * the member(s) matching the given organizationId
-     *
-     * @test
-     */
-    public function findByOrganizationIdWithCa() {
-        $organization_id = 1;
-
-        $expected_uri = "https://api.opentalent.fr/api/public/members_ca?_format=json&filter%5Bwhere%5D%5BorganizationId%5D=1&itemsPerPage=200";
-        $this->injectClientFor($expected_uri);
-
-        $actual = $this->repository->findByOrganizationId($organization_id, true);
-        $this->assertEquals($organization_id, $actual->getMembers()[0]->getOrganizationId());
-    }
-
     /**
      * memberToObject should return null if the given
      * member has not the good type

+ 4 - 4
ot_core/Tests/Unit/Domain/Repository/OrganizationRepositoryTest.php

@@ -17,7 +17,7 @@ class OrganizationRepositoryTest extends AbstractApiRepositoryTestCase
     public function findById() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/organizations?_format=json&filter%5Bwhere%5D%5Bid%5D=1&page=1&totalItems=1&itemsPerPage=8";
+        $expected_uri = "api/public/organizations?_format=json&filter%5Bwhere%5D%5Bid%5D=1&page=1&totalItems=1&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findById($organization_id);
@@ -32,7 +32,7 @@ class OrganizationRepositoryTest extends AbstractApiRepositoryTestCase
     public function findByName() {
         $name = 'a name';
 
-        $expected_uri = "https://api.opentalent.fr/api/public/organizations?_format=json&filter%5Bwhere%5D%5Bname%5D=a+name&page=1&totalItems=1&itemsPerPage=8";
+        $expected_uri = "api/public/organizations?_format=json&filter%5Bwhere%5D%5Bname%5D=a+name&page=1&totalItems=1&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findByName($name);
@@ -47,7 +47,7 @@ class OrganizationRepositoryTest extends AbstractApiRepositoryTestCase
     public function findByInexistantName() {
         $name = 'a unknown name';
 
-        $expected_uri = "https://api.opentalent.fr/api/public/organizations?_format=json&filter%5Bwhere%5D%5Bname%5D=a+unknown+name&page=1&totalItems=1&itemsPerPage=8";
+        $expected_uri = "api/public/organizations?_format=json&filter%5Bwhere%5D%5Bname%5D=a+unknown+name&page=1&totalItems=1&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findByName($name);
@@ -62,7 +62,7 @@ class OrganizationRepositoryTest extends AbstractApiRepositoryTestCase
     public function findChildrenById() {
         $organization_id = 1;
 
-        $expected_uri = "https://api.opentalent.fr/api/public/organizations?_format=json&parentId=1&children=1&page=1&itemsPerPage=8";
+        $expected_uri = "api/public/organizations?_format=json&parentId=1&children=1&page=1&itemsPerPage=8";
         $this->injectClientFor($expected_uri);
 
         $actual = $this->repository->findChildrenById($organization_id);

+ 30 - 30
ot_core/Tests/Unit/Fixtures/ApiResponseFixtures.php

@@ -160,39 +160,39 @@ class ApiResponseFixtures
      * @var string[]
      */
     private $map = [
-        'https://api.opentalent.fr/api/public/organizations?_format=json&itemsPerPage=10&foo=1' => 'stub',
-        'https://api.opentalent.fr/api/public/organizations?_format=json&itemsPerPage=8' => 'stub',
-        'https://api.opentalent.fr/api/public/organizations?_format=json&filter%5Bwhere%5D%5Bid%5D=1&page=1&totalItems=1&itemsPerPage=8' => 'org',
-        'https://api.opentalent.fr/api/public/organizations?_format=json&filter%5Bwhere%5D%5Bname%5D=a+name&page=1&totalItems=1&itemsPerPage=8' => 'org',
-        'https://api.opentalent.fr/api/public/organizations?_format=json&parentId=1&children=1&page=1&itemsPerPage=8' => 'org',
-        'https://api.opentalent.fr/api/public/organizations?_format=json&filter%5Bwhere%5D%5Bid%5D=1&itemsPerPage=8' => 'org',
-        'https://api.opentalent.fr/api/public/organizations?_format=json&filter%5Bwhere%5D%5Bname%5D=a+unknown+name&page=1&totalItems=1&itemsPerPage=8' => 'org',
-        'https://api.opentalent.fr/api/public/events?_format=json&filter%5Bwhere%5D%5Bid%5D=2&page=1&totalItems=1&itemsPerPage=8' => 'event',
-        'https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&page=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&itemsPerPage=8' => 'event',
-        'https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&page=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&parent=1&itemsPerPage=8' => 'event',
-        'https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&children=1&itemsPerPage=8' => 'event',
-        'https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&filter%5Bwhere%5D%5Bid%5D=1&itemsPerPage=8' => 'event',
-        'https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&page=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC' => 'event',
-        'https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&page=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&parent=1' => 'event',
-        'https://api.opentalent.fr/api/public/events?_format=json&organizationId=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&children=1' => 'event',
-        'https://api.opentalent.fr/api/public/donors?_format=json&organizationId=1&page=1&itemsPerPage=8' => 'donor',
-        'https://api.opentalent.fr/api/public/donors?_format=json&organizationId=1&parent=1&page=1&itemsPerPage=8' => 'donor',
-        'https://api.opentalent.fr/api/public/members?_format=json&filter%5Bwhere%5D%5BorganizationId%5D=1&itemsPerPage=200' => 'member',
-        'https://api.opentalent.fr/api/public/members_ca?_format=json&filter%5Bwhere%5D%5BorganizationId%5D=1&itemsPerPage=200' => 'member'
+        'api/public/organizations?_format=json' => 'stub',
+        'api/public/organizations?_format=json&itemsPerPage=10&foo=1' => 'stub',
+        'api/public/organizations?_format=json&itemsPerPage=8' => 'stub',
+        'api/public/organizations?_format=json&filter%5Bwhere%5D%5Bid%5D=1&page=1&totalItems=1&itemsPerPage=8' => 'org',
+        'api/public/organizations?_format=json&filter%5Bwhere%5D%5Bname%5D=a+name&page=1&totalItems=1&itemsPerPage=8' => 'org',
+        'api/public/organizations?_format=json&parentId=1&children=1&page=1&itemsPerPage=8' => 'org',
+        'api/public/organizations?_format=json&filter%5Bwhere%5D%5Bid%5D=1&itemsPerPage=8' => 'org',
+        'api/public/organizations?_format=json&filter%5Bwhere%5D%5Bname%5D=a+unknown+name&page=1&totalItems=1&itemsPerPage=8' => 'org',
+        'api/public/events?_format=json&filter%5Bwhere%5D%5Bid%5D=2&page=1&totalItems=1&itemsPerPage=8' => 'event',
+        'api/public/events?_format=json&organizationId=1&page=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&itemsPerPage=8' => 'event',
+        'api/public/events?_format=json&organizationId=1&page=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&parent=1&itemsPerPage=8' => 'event',
+        'api/public/events?_format=json&organizationId=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&children=1&itemsPerPage=8' => 'event',
+        'api/public/events?_format=json&organizationId=1&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&filter%5Bwhere%5D%5Bid%5D=1&itemsPerPage=8' => 'event',
+        'api/public/events?_format=json&organizationId=1&page=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC' => 'event',
+        'api/public/events?_format=json&organizationId=1&page=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&parent=1' => 'event',
+        'api/public/events?_format=json&organizationId=1&itemsPerPage=1&filter%5Bwhere%5D%5BdatetimeStart%5D%5Bgte%5D=2021-01-01T00%3A00%3A00%2B00%3A00&filter%5Bwhere%5D%5BdatetimeEnd%5D%5Blte%5D=2021-01-31T00%3A00%3A00%2B00%3A00&filter%5Border%5D%5B0%5D%5BdatetimeStart%5D=ASC&children=1' => 'event',
+        'api/public/donors?_format=json&organizationId=1&page=1&itemsPerPage=8' => 'donor',
+        'api/public/donors?_format=json&organizationId=1&parent=1&page=1&itemsPerPage=8' => 'donor',
+        'api/public/members?_format=json&filter%5Bwhere%5D%5BorganizationId%5D=1&itemsPerPage=200' => 'member',
+        'api/public/members_ca?_format=json&filter%5Bwhere%5D%5BorganizationId%5D=1&itemsPerPage=200' => 'member'
     ];
 
     public function get($url) {
-        if (array_key_exists($url, $this->map)) {
-            $query = $this->map[$url];
-            $response = new HtmlResponse(
-                $this->responses[$query],
-                200,
-                []
-            );
-            return $response;
-        } else {
-            return null;
+        foreach ($this->map as $needle => $response_key) {
+            if (str_ends_with($url, $needle))
+            {
+                return new HtmlResponse(
+                    $this->responses[$response_key],
+                    200,
+                    []
+                );
+            }
         }
+        throw new \RuntimeException("No fixture result for uri " . $url);
     }
-
 }

+ 163 - 0
ot_core/Tests/Unit/Service/OpentalentApiServiceTest.php

@@ -0,0 +1,163 @@
+<?php
+
+namespace Opentalent\OtCore\Tests\Unit\Service;
+
+use GuzzleHttp\Client;
+use Nimut\TestingFramework\TestCase\UnitTestCase;
+use Opentalent\OtCore\Exception\ApiRequestException;
+use Opentalent\OtCore\Service\OpentalentApiService;
+use Opentalent\OtCore\Tests\Unit\Fixtures\ApiResponseFixtures;
+use PHPUnit\TextUI\RuntimeException;
+use Prophecy\Argument;
+use TYPO3\CMS\Core\Core\ApplicationContext;
+
+class OpentalentApiServiceTest extends UnitTestCase
+{
+    protected $context;
+    /**
+     * @var ApiResponseFixtures
+     */
+    protected $fixture;
+    /**
+     * @var \Prophecy\Prophecy\ObjectProphecy
+     */
+    protected $client;
+
+    protected $service;
+
+    public function setUp() {
+        // mock the application context
+        $this->context = $this->prophesize(ApplicationContext::class);
+        $this->context->isProduction()->willReturn(false);
+        $this->context->isDevelopment()->willReturn(false);
+        $this->context->isTesting()->willReturn(true);
+
+        $this->client = $this->prophesize(Client::class);
+
+        $this->service = new OpentalentApiService($this->client->reveal(), $this->context->reveal());
+
+        $this->fixture = new ApiResponseFixtures();
+    }
+
+    protected function injectClientFor($uri, $http_method='GET') {
+        // mock the Guzzle client
+        $client = $this->prophesize(Client::class);
+        try {
+            $willReturn = $this->fixture->get($uri);
+            $client->request($http_method, $uri)
+                ->shouldBeCalled()
+                ->willReturn($willReturn);
+
+        } catch (\RuntimeException $e) {
+            $client->request($http_method, Argument::type('string'))
+                ->shouldBeCalled()
+                ->willThrow(new \GuzzleHttp\Exception\TransferException('error'));
+        }
+
+        $reflectionObject = new \ReflectionObject($this->service);
+        $reflectionMethod = $reflectionObject->getMethod('injectClient');
+        $reflectionMethod->setAccessible(true);
+
+        $reflectionMethod->invokeArgs($this->service, [$client->reveal()]);
+    }
+
+    /**
+     * get should build a valid url, send a query and
+     * return a Guzzle response object
+     *
+     * @test
+     */
+    public function get() {
+
+        $base_uri = "api/public/organizations";
+        $params = ['itemsPerPage' => 10, 'foo' => 1];
+
+        // uri as it is supposed to be processed by the repo
+        $processed_uri = $base_uri . "?_format=json&itemsPerPage=10&foo=1";
+
+        $this->injectClientFor($processed_uri);
+        $actual = $this->service->request('GET', $base_uri, $params);
+
+        $this->assertEquals(200, $actual->getStatusCode());
+    }
+
+    /**
+     * get should build a valid url, send a query and
+     * return a Guzzle response object
+     *
+     * @test
+     */
+    public function getWithNoParams() {
+
+        $base_uri = "api/public/organizations";
+        $params = [];
+
+        // uri as it is supposed to be processed by the repo
+        $processed_uri = $base_uri . "?_format=json";
+
+        $this->injectClientFor($processed_uri);
+        $actual = $this->service->request('GET', $base_uri, $params);
+
+        $this->assertEquals(200, $actual->getStatusCode());
+    }
+
+    /**
+     * get should build a valid url, send a query and
+     * return a Guzzle response object
+     *
+     * @test
+     */
+    public function getInvalidUri()
+    {
+        $base_uri = "a very bad uri";
+        $params = [];
+
+        $this->injectClientFor($base_uri);
+
+        try {
+            $res = $this->service->request('GET', $base_uri, $params);
+            throw new \AssertionError("An ApiRequestException should have been thrown");
+        } catch (ApiRequestException $e) {
+            $this->assertEquals('error', $e->getMessage());
+        }
+    }
+
+    /**
+     * getBody should return the response body as a string
+     *
+     * @test
+     */
+    public function getBody() {
+
+        $base_uri = "api/public/organizations";
+        $params = ['itemsPerPage' => 10, 'foo' => 1];
+
+        // uri as it is supposed to be processed by the repo
+        $processed_uri = $base_uri . "?_format=json&itemsPerPage=10&foo=1";
+
+        $this->injectClientFor($processed_uri);
+        $actual = $this->service->getBody($base_uri, $params);
+
+        $this->assertEquals('{"@context": "/api/contexts/PortailOrganization"}', $actual);
+    }
+
+    /**
+     * getBody should return the response body as an array
+     *
+     * @test
+     */
+    public function getJsonDecoded() {
+
+        $base_uri = "api/public/organizations";
+        $params = ['itemsPerPage' => 10, 'foo' => 1];
+
+        // uri as it is supposed to be processed by the repo
+        $processed_uri = $base_uri . "?_format=json&itemsPerPage=10&foo=1";
+
+        $this->injectClientFor($processed_uri);
+        $actual = $this->service->getJsonDecoded($base_uri, $params);
+
+        $this->assertEquals(["@context" => "/api/contexts/PortailOrganization"], $actual);
+    }
+
+}