Explorar o código

complete revision of DolibarrSyncService

Olivier Massot %!s(int64=3) %!d(string=hai) anos
pai
achega
438e0dd569

+ 4 - 1
src/Service/Dolibarr/DolibarrSyncService.php

@@ -327,6 +327,10 @@ class DolibarrSyncService
             if ($operation->getStatus() !== $operation::STATUS_READY) {
                 // operation has already been treated
                 $this->logger->warning('Tried to execute an operation that was not marked as ready : ' . $operation);
+                $i++;
+                if ($progressionCallback !== null) {
+                    $progressionCallback($i, $total);
+                }
                 continue;
             }
 
@@ -557,7 +561,6 @@ class DolibarrSyncService
             foreach ($contactPoints as $contactPoint) {
                 if ($contactPoint->getContactType() === $contactType) {
                     if ($contactPoint->getTelphone() !== null) {
-
                         return $this->formatPhoneNumber($contactPoint->getTelphone());
                     }
                     if ($contactPoint->getMobilPhone() !== null) {

+ 358 - 159
tests/Service/Dolibarr/DolibarrSyncServiceTest.php

@@ -1,4 +1,4 @@
-<?php
+<?php /** @noinspection ALL */
 
 namespace App\Tests\Service\Dolibarr;
 
@@ -24,7 +24,9 @@ use App\Repository\Organization\OrganizationRepository;
 use App\Service\Core\AddressPostalUtils;
 use App\Service\Dolibarr\DolibarrApiService;
 use App\Service\Dolibarr\DolibarrSyncService;
+use App\Service\Rest\Operation\BaseRestOperation;
 use App\Service\Rest\Operation\CreateOperation;
+use App\Service\Rest\Operation\DeleteOperation;
 use App\Service\Rest\Operation\UpdateOperation;
 use App\Service\Utils\ArrayUtils;
 use Doctrine\Common\Collections\ArrayCollection;
@@ -33,6 +35,7 @@ use libphonenumber\PhoneNumberUtil;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
+use RuntimeException;
 use Symfony\Contracts\HttpClient\ResponseInterface;
 use Symfony\Contracts\Translation\TranslatorInterface;
 
@@ -49,6 +52,7 @@ class TestableDolibarrSyncService extends DolibarrSyncService {
     public function getPersonContact(Person $person): ?ContactPoint { return parent::getPersonContact($person); }
     public function formatContactPosition(array $missions, ?string $gender = 'X'): string { return parent::formatContactPosition($missions, $gender); }
     public function formatPhoneNumber(PhoneNumber $phoneNumber): string { return parent::formatPhoneNumber($phoneNumber); }
+    public function validateResponse(ResponseInterface $response, BaseRestOperation $operation): void { parent::validateResponse($response, $operation); }
 }
 
 class DolibarrSyncServiceTest extends TestCase
@@ -94,6 +98,11 @@ class DolibarrSyncServiceTest extends TestCase
         $this->logger->method('error')->willReturnSelf();
     }
 
+    /**
+     * Full test of the scan method
+     *
+     * @throws \Exception
+     */
     public function testScan(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -557,7 +566,13 @@ class DolibarrSyncServiceTest extends TestCase
         $this->assertCount(0, $progressionCallbackExpectedCalls);
     }
 
-    public function testExecuteError()
+    /**
+     * All valid operations shall be executed
+     * If an operation is not in status READY, a warning shall be logged, and the operation shall be skipped
+     *
+     * @throws \Exception
+     */
+    public function testExecute(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -565,162 +580,256 @@ class DolibarrSyncServiceTest extends TestCase
             ->setMethodsExcept(['execute'])
             ->getMock();
 
-        $operation = new CreateOperation('operation 1', 'thirdparty', ['data' => 1]);
-        $this->assertEquals($operation->getStatus(), $operation::STATUS_READY);
+        $progressionCallbackExpectedCalls = [[1, 3], [2, 3], [3, 3]];
+
+        $progressionCallback = static function ($i, $total) use (&$progressionCallbackExpectedCalls) {
+            [$expectedI, $expectedTotal] = array_shift($progressionCallbackExpectedCalls);
+            if ($i !== $expectedI || $total !== $expectedTotal) {
+                throw new \AssertionError(
+                    'Progression callback error, expected parameters are (' . $expectedI . ',' . $expectedTotal . ')' .
+                    ', got (' . $i . ', ' . $total . ')'
+                );
+            }
+        };
+
+        $response = $this->getMockBuilder(ResponseInterface::class)->disableOriginalConstructor()->getMock();
+
+        $operation1 = $this->getMockBuilder(UpdateOperation::class)->disableOriginalConstructor()->getMock();
+        $operation1->method('getStatus')->willReturnOnConsecutiveCalls(BaseRestOperation::STATUS_READY, BaseRestOperation::STATUS_DONE);
+        $operation1->method('getChangeLog')->willReturn(['foo']);
+        $operation1->expects(self::once())->method('execute')->willReturn($response);
+
+        $dolibarrSyncService->method('validateResponse')->with($response, $operation1)->willThrowException(new RuntimeException());
+
+        $operation2 = $this->getMockBuilder(CreateOperation::class)->disableOriginalConstructor()->getMock();
+        $operation2->method('getStatus')->willReturn(
+            BaseRestOperation::STATUS_READY, BaseRestOperation::STATUS_ERROR, BaseRestOperation::STATUS_ERROR // An error happened
+        );
+        $operation2->expects(self::once())->method('execute');
+
+        $operation3 = $this->getMockBuilder(DeleteOperation::class)->disableOriginalConstructor()->getMock();
+        $operation3->method('getStatus')->willReturn(BaseRestOperation::STATUS_DONE); // Invalid status, should log a warning and not execute
+        $operation3->expects(self::never())->method('execute');
 
-        $responseError = $this->getMockBuilder(ResponseInterface::class)->getMock();
-        $responseError->method('getStatusCode')->willReturn(500);
-        $this->dolibarrApiService->method('request')->willReturn($responseError);
+        $this->logger->expects(self::exactly(3))->method('warning'); // 1 warning from validateResponse on the Update Op, and 2 because of the bad status of the Create Op
+        $this->logger->expects(self::exactly(3))->method('error'); // The exception thrown during the execution of the Delete op will log 3 errors
 
-        // POST operation will returned a server error
-        $operation = $dolibarrSyncService->execute([$operation])[0];
-        $this->assertEquals($operation::STATUS_ERROR, $operation->getStatus());
+        $dolibarrSyncService->execute([$operation1, $operation2, $operation3], $progressionCallback);
     }
 
-    public function testExecuteInvalid()
-    {
+    public function testRun() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
                 $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
-            ->setMethodsExcept(['execute'])
+            ->setMethodsExcept(['run'])
             ->getMock();
 
-        $operation = new UpdateOperation('operation 1', 'thirdparty', 1, ['data' => 1]);
-        $responseInvalid = $this->getMockBuilder(ResponseInterface::class)->getMock();
-        $responseInvalid->method('getStatusCode')->willReturn(200);
-        $responseInvalid->method('toArray')->willReturn(['data' => 0]);
-        $this->dolibarrApiService->method('request')->willReturn($responseInvalid);
+        $operations = ['operation1', 'operation2'];
 
-        // POST operation will return a different content that the one which were posted, this should log a warning
-        $this->logger->expects($this->once())->method('warning');
+        $dolibarrSyncService->expects(self::once())->method('scan')->willReturn($operations);
+        $dolibarrSyncService->expects(self::once())->method('execute')->with($operations);
 
-        $operation = $dolibarrSyncService->execute([$operation])[0];
-        $this->assertEquals($operation::STATUS_DONE, $operation->getStatus());
+        $result = $dolibarrSyncService->run();
+
+        $this->assertEquals($operations, $result);
     }
 
-    public function testExecuteOk() {
+    public function testGetDolibarrSocietiesIndex(): void
+    {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
                 $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
-            ->setMethodsExcept(['execute'])
+            ->setMethodsExcept(['getDolibarrSocietiesIndex'])
             ->getMock();
 
-        $progressionCallbackExpectedCalls = [[1, 1]];
+        $this->dolibarrApiService
+            ->expects($this->once())
+            ->method('getAllClients')
+            ->willReturn(
+                [
+                    ['id' => 1, 'array_options' => ['options_2iopen_organization_id' => 101]],
+                    ['id' => 2, 'array_options' => ['options_2iopen_organization_id' => 102]],
+                    ['id' => 3, 'array_options' => ['options_2iopen_organization_id' => null]], // No org id but also no contract, so it's ok
+                    ['id' => 4, 'name' => 'foo', 'array_options' => ['options_2iopen_organization_id' => null]], // No org id but has a contract, a warning should be logged
+                ]
+            );
 
-        $progressionCallback = static function ($i, $total) use (&$progressionCallbackExpectedCalls) {
-            [$expectedI, $expectedTotal] = array_shift($progressionCallbackExpectedCalls);
-            if ($i !== $expectedI || $total !== $expectedTotal) {
-                throw new \AssertionError(
-                    'Progression callback error, expected parameters are (' . $expectedI . ',' . $expectedTotal . ')' .
-                    ', got (' . $i . ', ' . $total . ')'
-                );
-            }
-        };
+        $this->dolibarrApiService->expects(self::exactly(2))->method('getActiveContract')->willReturnMap([
+            [3, null],
+            [4, ['dummy non-empty data']]
+        ]);
+
+        $this->logger->expects(self::once())->method('warning')->with('Dolibarr client has no organization id: foo (4)');
 
-        $operation = new CreateOperation('operation 1', 'thirdparty', ['data' => 1]);
-        $responseOk = $this->getMockBuilder(ResponseInterface::class)->getMock();
-        $responseOk->method('getStatusCode')->willReturn(200);
-        $responseOk->method('toArray')->willReturn(['data' => 1]);
-        $this->dolibarrApiService->method('request')->willReturn($responseOk);
+        $index = $dolibarrSyncService->getDolibarrSocietiesIndex();
+
+        $this->assertEqualsCanonicalizing(
+            [
+                1 => ['id' => 1, 'array_options' => ['options_2iopen_organization_id' => 101]],
+                2 => ['id' => 2, 'array_options' => ['options_2iopen_organization_id' => 102]]
+            ],
+            $index
+        );
 
-        $operation = $dolibarrSyncService->execute([$operation])[0];
-        $this->assertEquals($operation::STATUS_DONE, $operation->getStatus());
     }
 
-    public function testGetDolibarrSocietiesIndex() {
-        $this->dolibarrApiService
+    public function testGetActiveMembersIndex(): void
+    {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getActiveMembersIndex'])
+            ->getMock();
+
+        $this->accessRepository
             ->expects($this->once())
-            ->method('getAllClients')
+            ->method('getAllActiveMembersAndMissions')
             ->willReturn(
-                $this->getJsonContentFromFixture('thirdparties.json')
+                [
+                    ['id' => 1, 'organization_id' => 1, 'mission' => FunctionEnum::PRESIDENT()->getValue()],
+                    ['id' => 2, 'organization_id' => 1, 'mission' => FunctionEnum::STUDENT()->getValue()],
+                    ['id' => 3, 'organization_id' => 2, 'mission' => FunctionEnum::PRESIDENT()->getValue()],
+                    ['id' => 3, 'organization_id' => 2, 'mission' => FunctionEnum::TEACHER()->getValue()]
+                ]
             );
 
-        $syncService = $this->newDolibarrSyncService();
-
-        $index = $syncService->getDolibarrSocietiesIndex();
+        $index = $dolibarrSyncService->getActiveMembersIndex();
 
-        $this->assertEquals("13930", $index[13930]['array_options']['options_2iopen_organization_id']);
+        $this->assertEqualsCanonicalizing([
+            1 => [1 => [FunctionEnum::PRESIDENT()->getValue()], 2 => [FunctionEnum::STUDENT()->getValue()]],
+            2 => [3 => [FunctionEnum::PRESIDENT()->getValue(), FunctionEnum::TEACHER()->getValue()]]
+        ], $index);
     }
 
-    public function testFindDolibarrContactFor() {
+    public function testFindDolibarrContactForById(): void
+    {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['findDolibarrContactFor'])
+            ->getMock();
 
-        $contacts = $this->getJsonContentFromFixture('contacts.json');
+        $contacts = [
+            ['id' => 1, 'array_options' => ['options_2iopen_person_id' => 101]],
+            ['id' => 2, 'array_options' => ['options_2iopen_person_id' => 102]],
+        ];
 
         // Find by id
-        $person1 = $this->getMockBuilder(Person::class)->getMock();
-        $person1->method('getId')->willReturn(108939);
+        $person = $this->getMockBuilder(Person::class)->getMock();
+        $person->method('getId')->willReturn(102);
 
-        $contact1 = TestableDolibarrSyncService::findDolibarrContactFor($contacts, $person1);
-        $this->assertEquals("5868", $contact1['id']);
+        $contact = $dolibarrSyncService->findDolibarrContactFor($contacts, $person);
+        $this->assertEquals(2, $contact['id']);
+    }
 
-        // Find by full name (contact already has another person id, it should not be returned)
-        $person2 = $this->getMockBuilder(Person::class)->getMock();
-        $person2->method('getId')->willReturn(-1);
-        $person2->method('getName')->willReturn('dupont');
-        $person2->method('getGivenName')->willReturn('Valerie');
+    public function testFindDolibarrContactForByName(): void
+    {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['findDolibarrContactFor'])
+            ->getMock();
 
-        $contact2 = TestableDolibarrSyncService::findDolibarrContactFor($contacts, $person2);
-        $this->assertEquals(null, $contact2);
+        $contacts = [
+            ['id' => 1, 'firstname' => 'mister', 'lastname' => 'X', 'array_options' => ['options_2iopen_person_id' => null]],
+            ['id' => 2, 'firstname' => 'miss', 'lastname' => 'Y', 'array_options' => ['options_2iopen_person_id' => null]],
+        ];
 
         // Find by full name (contact has no person id, it should be returned)
-        $person3 = $this->getMockBuilder(Person::class)->getMock();
-        $person3->method('getId')->willReturn(-1);
-        $person3->method('getName')->willReturn('ZORRO');
-        $person3->method('getGivenName')->willReturn('Fabrice');
+        $person = $this->getMockBuilder(Person::class)->getMock();
+        $person->method('getId')->willReturn(101);
+        $person->method('getName')->willReturn('X');
+        $person->method('getGivenName')->willReturn('mister');
 
-        $contact3 = TestableDolibarrSyncService::findDolibarrContactFor($contacts, $person3);
-        $this->assertEquals("5872", $contact3['id']);
+        $result = $dolibarrSyncService->findDolibarrContactFor($contacts, $person);
 
-        // Do not find
-        $person4 = $this->getMockBuilder(Person::class)->getMock();
-        $person4->method('getId')->willReturn(-1);
+        $this->assertEquals(1, $result['id']);
+    }
+
+    public function testFindDolibarrContactForByNameWithConflict(): void
+    {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['findDolibarrContactFor'])
+            ->getMock();
+
+        $contacts = [
+            ['id' => 1, 'firstname' => 'mister', 'lastname' => 'X', 'array_options' => ['options_2iopen_person_id' => 1]],
+            ['id' => 2, 'firstname' => 'miss', 'lastname' => 'Y', 'array_options' => ['options_2iopen_person_id' => 2]],
+        ];
+
+        // Find by full name (contact already has another person id, it should not be returned)
+        $person = $this->getMockBuilder(Person::class)->getMock();
+        $person->method('getId')->willReturn(101);
+        $person->method('getName')->willReturn('X');
+        $person->method('getGivenName')->willReturn('mister');
+
+        $result = $dolibarrSyncService->findDolibarrContactFor($contacts, $person);
+
+        $this->assertEquals(null, $result);
 
-        $contact4 = TestableDolibarrSyncService::findDolibarrContactFor($contacts, $person4);
-        $this->assertEquals(null, $contact4);
     }
 
-    public function testActiveMembersIndex() {
-        $this->accessRepository
-            ->expects($this->once())
-            ->method('getAllActiveMembersAndMissions')
-            ->willReturn(
-                [
-                    ['id' => 123, 'organization_id' => 1, 'mission' => FunctionEnum::PRESIDENT()->getValue()],
-                    ['id' => 123, 'organization_id' => 1, 'mission' => FunctionEnum::TEACHER()->getValue()],
-                    ['id' => 124, 'organization_id' => 1, 'mission' => FunctionEnum::ADMINISTRATIVE_STAFF()->getValue()],
-                    ['id' => 125, 'organization_id' => 2, 'mission' => FunctionEnum::ADHERENT()->getValue()],
-                ]
-            );
+    public function testFindDolibarrContactNotFound(): void
+    {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['findDolibarrContactFor'])
+            ->getMock();
 
-        $syncService = $this->newDolibarrSyncService();
+        $contacts = [
+            ['id' => 1, 'firstname' => 'mister', 'lastname' => 'X', 'array_options' => ['options_2iopen_person_id' => 1]],
+            ['id' => 2, 'firstname' => 'miss', 'lastname' => 'Y', 'array_options' => ['options_2iopen_person_id' => 2]],
+        ];
 
-        $this->assertEquals(
-            [
-                1 => [
-                    123 => [FunctionEnum::PRESIDENT()->getValue(), FunctionEnum::TEACHER()->getValue()],
-                    124 => [FunctionEnum::ADMINISTRATIVE_STAFF()->getValue()]
-                ],
-                2 => [
-                    125 => [FunctionEnum::ADHERENT()->getValue()]
-                ]
-            ],
-            $syncService->getActiveMembersIndex()
-        );
+        // Do not find
+        $person = $this->getMockBuilder(Person::class)->getMock();
+        $person->method('getId')->willReturn(-1);
+        $person->method('getName')->willReturn('Presley');
+        $person->method('getGivenName')->willReturn('Elvis');
+
+        $result = $dolibarrSyncService->findDolibarrContactFor($contacts, $person);
+
+        $this->assertEquals(null, $result);
     }
 
-    public function testSanitizeDolibarrData() {
+    public function testSanitizeDolibarrData(): void
+    {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['sanitizeDolibarrData'])
+            ->getMock();
 
-        $this->assertEquals(null, TestableDolibarrSyncService::sanitizeDolibarrData(null));
+        $result = $dolibarrSyncService->sanitizeDolibarrData(['a' => 'A', 'b' => '', 'c' => ['d' => 'D', 'e' => '']]);
 
         $this->assertEquals(
             ['a' => 'A', 'b' => null, 'c' => ['d' => 'D', 'e' => null]],
-            TestableDolibarrSyncService::sanitizeDolibarrData(
-                ['a' => 'A', 'b' => '', 'c' => ['d' => 'D', 'e' => '']]
-            )
+            $result
         );
     }
 
-    public function testGetOrganizationPostalAddress() {
+    public function testSanitizeDolibarrDataWithNull(): void
+    {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['sanitizeDolibarrData'])
+            ->getMock();
+
+        $result = $dolibarrSyncService->sanitizeDolibarrData(null);
+        $this->assertEquals(null, $result);
+    }
+
+    public function testGetOrganizationPostalAddress(): void
+    {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getOrganizationPostalAddress'])
+            ->getMock();
 
         $organization = $this->getMockBuilder(Organization::class)->getMock();
         $organizationAddressPostal1 = $this->getMockBuilder(OrganizationAddressPostal::class)->getMock();
@@ -740,26 +849,38 @@ class DolibarrSyncServiceTest extends TestCase
                 new ArrayCollection([$organizationAddressPostal1, $organizationAddressPostal2, $organizationAddressPostal3])
             );
 
-        $syncService = $this->newDolibarrSyncService($organization);
-
         $this->assertEquals(
             $addressPostal,
-            $syncService->getOrganizationPostalAddress($organization)
+            $dolibarrSyncService->getOrganizationPostalAddress($organization)
         );
+    }
 
-        $organization2 = $this->getMockBuilder(Organization::class)->getMock();
-        $organization2->expects($this->once())
+    public function testGetOrganizationPostalAddressNoResult(): void
+    {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getOrganizationPostalAddress'])
+            ->getMock();
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->expects($this->once())
             ->method('getOrganizationAddressPostals')
             ->willReturn(new ArrayCollection([]));
 
         $this->assertEquals(
             null,
-            $syncService->getOrganizationPostalAddress($organization2)
+            $dolibarrSyncService->getOrganizationPostalAddress($organization)
         );
     }
-    public function testGetOrganizationPhoneWithExistingPhone()
+
+    public function testGetOrganizationPhoneWithExistingPhone(): void
     {
-        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getOrganizationPhone'])
+            ->getMock();
 
         $contactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
         $contactPoint2 = $this->getMockBuilder(ContactPoint::class)->getMock();
@@ -769,10 +890,10 @@ class DolibarrSyncServiceTest extends TestCase
         $contactPoint2->method('getContactType')->willReturn(ContactPointTypeEnum::BILL()->getValue());
         $contactPoint3->method('getContactType')->willReturn(ContactPointTypeEnum::PRINCIPAL()->getValue());
 
-        $phoneUtil = PhoneNumberUtil::getInstance();
-        $phoneNumber = $phoneUtil->parse('0161626365', "FR");
-        $contactPoint2->method('getTelphone')->willReturn($phoneNumber);
+        $phone = $this->getMockBuilder(PhoneNumber::class)->disableOriginalConstructor()->getMock();
+        $contactPoint2->method('getTelphone')->willReturn($phone);
 
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
         $organization
             ->expects($this->once())
             ->method('getContactPoints')
@@ -780,16 +901,20 @@ class DolibarrSyncServiceTest extends TestCase
                 new ArrayCollection([$contactPoint1, $contactPoint2, $contactPoint3])
             );
 
-        $syncService = $this->newDolibarrSyncService();
+        $dolibarrSyncService->expects(self::once())->method('formatPhoneNumber')->with($phone)->willReturn('+33161626365');
 
         $this->assertEquals(
             '+33161626365',
-            $syncService->getOrganizationPhone($organization)
+            $dolibarrSyncService->getOrganizationPhone($organization)
         );
     }
 
     public function testGetOrganizationPhoneWithMobilePhone() {
-        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getOrganizationPhone'])
+            ->getMock();
 
         $contactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
         $contactPoint2 = $this->getMockBuilder(ContactPoint::class)->getMock();
@@ -801,10 +926,10 @@ class DolibarrSyncServiceTest extends TestCase
 
         $contactPoint2->expects($this->once())->method('getTelphone')->willReturn(null);
 
-        $phoneUtil = PhoneNumberUtil::getInstance();
-        $phoneNumber = $phoneUtil->parse('0661626365', "FR");
-        $contactPoint2->method('getMobilPhone')->willReturn($phoneNumber);
+        $mobilePhone = $this->getMockBuilder(PhoneNumber::class)->disableOriginalConstructor()->getMock();
+        $contactPoint2->method('getMobilPhone')->willReturn($mobilePhone);
 
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
         $organization
             ->expects($this->once())
             ->method('getContactPoints')
@@ -812,31 +937,41 @@ class DolibarrSyncServiceTest extends TestCase
                 new ArrayCollection([$contactPoint1, $contactPoint2, $contactPoint3])
             );
 
-        $syncService = $this->newDolibarrSyncService();
+        $dolibarrSyncService->expects(self::once())->method('formatPhoneNumber')->with($mobilePhone)->willReturn('+33661626365');
 
         $this->assertEquals(
             '+33661626365',
-            $syncService->getOrganizationPhone($organization)
+            $dolibarrSyncService->getOrganizationPhone($organization)
         );
     }
 
     public function testGetOrganizationPhoneWithNoPhone() {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getOrganizationPhone'])
+            ->getMock();
+
         $organization = $this->getMockBuilder(Organization::class)->getMock();
         $organization
             ->expects($this->once())
             ->method('getContactPoints')
             ->willReturn(new ArrayCollection([]));
 
-        $syncService = $this->newDolibarrSyncService();
+        $dolibarrSyncService->expects(self::never())->method('formatPhoneNumber');
 
         $this->assertEquals(
             null,
-            $syncService->getOrganizationPhone($organization)
+            $dolibarrSyncService->getOrganizationPhone($organization)
         );
     }
 
     public function testGetOrganizationEmailWithExistingEmail() {
-        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getOrganizationEmail'])
+            ->getMock();
 
         $contactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
         $contactPoint2 = $this->getMockBuilder(ContactPoint::class)->getMock();
@@ -848,6 +983,7 @@ class DolibarrSyncServiceTest extends TestCase
 
         $contactPoint2->method('getEmail')->willReturn('email@email.com');
 
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
         $organization
             ->expects($this->once())
             ->method('getContactPoints')
@@ -855,31 +991,38 @@ class DolibarrSyncServiceTest extends TestCase
                 new ArrayCollection([$contactPoint1, $contactPoint2, $contactPoint3])
             );
 
-        $syncService = $this->newDolibarrSyncService();
-
         $this->assertEquals(
             'email@email.com',
-            $syncService->getOrganizationEmail($organization)
+            $dolibarrSyncService->getOrganizationEmail($organization)
         );
     }
 
     public function testGetOrganizationEmailWithNoEmail() {
-        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getOrganizationEmail'])
+            ->getMock();
 
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
         $organization
             ->expects($this->once())
             ->method('getContactPoints')
             ->willReturn(new ArrayCollection([]));
 
-        $syncService = $this->newDolibarrSyncService();
-
         $this->assertEquals(
             null,
-            $syncService->getOrganizationEmail($organization)
+            $dolibarrSyncService->getOrganizationEmail($organization)
         );
     }
 
     public function testGetOrganizationNetworkId() {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getOrganizationNetworkId'])
+            ->getMock();
+
         $organization = $this->getMockBuilder(Organization::class)->getMock();
         $network = $this->getMockBuilder(Network::class)->getMock();
         $network->method('getId')->willReturn(3);
@@ -887,16 +1030,18 @@ class DolibarrSyncServiceTest extends TestCase
         $networkOrganization->method('getNetwork')->willReturn($network);
         $organization->method('getNetworkOrganizations')->willReturn(new ArrayCollection([$networkOrganization]));
 
-        $syncService = $this->newDolibarrSyncService();
-
         $this->assertEquals(
             3,
-            $syncService->getOrganizationNetworkId($organization)
+            $dolibarrSyncService->getOrganizationNetworkId($organization)
         );
     }
 
     public function testGetOrganizationNetworkIdWithMultipleResult() {
-        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getOrganizationNetworkId'])
+            ->getMock();
 
         $network1 = $this->getMockBuilder(Network::class)->getMock();
         $network1->method('getId')->willReturn(3);
@@ -910,19 +1055,24 @@ class DolibarrSyncServiceTest extends TestCase
         $networkOrganization2->method('getNetwork')->willReturn($network2);
         $networkOrganization2->method('getEndDate')->willReturn(null);
 
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
         $organization->method('getNetworkOrganizations')->willReturn(
             new ArrayCollection([$networkOrganization1, $networkOrganization2])
         );
 
-        $syncService = $this->newDolibarrSyncService();
-
         $this->assertEquals(
             4,
-            $syncService->getOrganizationNetworkId($organization)
+            $dolibarrSyncService->getOrganizationNetworkId($organization)
         );
     }
 
     public function testGetOrganizationNetworkIdWithNoResult() {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getOrganizationNetworkId'])
+            ->getMock();
+
         $organization = $this->getMockBuilder(Organization::class)->getMock();
         $network = $this->getMockBuilder(Network::class)->getMock();
         $network->method('getId')->willReturn(3);
@@ -931,15 +1081,19 @@ class DolibarrSyncServiceTest extends TestCase
         $networkOrganization->method('getEndDate')->willReturn(new \DateTime('2000-01-01'));
         $organization->method('getNetworkOrganizations')->willReturn(new ArrayCollection([$networkOrganization]));
 
-        $syncService = $this->newDolibarrSyncService();
-
         $this->assertEquals(
             null,
-            $syncService->getOrganizationNetworkId($organization)
+            $dolibarrSyncService->getOrganizationNetworkId($organization)
         );
     }
 
     public function testCountWithMission() {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['countWithMission'])
+            ->getMock();
+
         $members = [
             123 => [FunctionEnum::PRESIDENT()->getValue(), FunctionEnum::TEACHER()->getValue()],
             124 => [FunctionEnum::TEACHER()->getValue()],
@@ -949,12 +1103,12 @@ class DolibarrSyncServiceTest extends TestCase
 
         $this->assertEquals(
             2,
-            TestableDolibarrSyncService::countWithMission([FunctionEnum::TEACHER()->getValue()], $members)
+            $dolibarrSyncService->countWithMission([FunctionEnum::TEACHER()->getValue()], $members)
         );
 
         $this->assertEquals(
             3,
-            TestableDolibarrSyncService::countWithMission(
+            $dolibarrSyncService->countWithMission(
                 [FunctionEnum::TEACHER()->getValue(), FunctionEnum::TREASURER()->getValue()],
                 $members
             )
@@ -962,16 +1116,22 @@ class DolibarrSyncServiceTest extends TestCase
 
         $this->assertEquals(
             1,
-            TestableDolibarrSyncService::countWithMission([FunctionEnum::STUDENT()->getValue()], $members)
+            $dolibarrSyncService->countWithMission([FunctionEnum::STUDENT()->getValue()], $members)
         );
 
         $this->assertEquals(
             0,
-            TestableDolibarrSyncService::countWithMission([FunctionEnum::ARCHIVIST()->getValue()], $members)
+            $dolibarrSyncService->countWithMission([FunctionEnum::ARCHIVIST()->getValue()], $members)
         );
     }
 
     public function testGetPersonContact() {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['getPersonContact'])
+            ->getMock();
+
         $person = $this->getMockBuilder(Person::class)->getMock();
 
         $contactPoint1 = $this->getMockBuilder(ContactPoint::class)->getMock();
@@ -982,19 +1142,27 @@ class DolibarrSyncServiceTest extends TestCase
 
         $person->expects($this->once())->method('getContactPoints')->willReturn(new ArrayCollection([$contactPoint1, $contactPoint2]));
 
-        $syncService = $this->newDolibarrSyncService();
-
         $this->assertEquals(
             $contactPoint2,
-            $syncService->getPersonContact($person)
+            $dolibarrSyncService->getPersonContact($person)
         );
 
         $person2 = $this->getMockBuilder(Person::class)->getMock();
         $person2->expects($this->once())->method('getContactPoints')->willReturn(new ArrayCollection([]));
-        $this->assertEquals(null, $syncService->getPersonContact($person2));
+
+        $this->assertEquals(
+            null,
+            $dolibarrSyncService->getPersonContact($person2)
+        );
     }
 
     public function testFormatContactPosition() {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['formatContactPosition'])
+            ->getMock();
+
         $this->translator->method('trans')->willReturnMap(
             [
                 [FunctionEnum::PRESIDENT()->getValue(), ['gender' => 'X'], null, null, 'Président(e)'],
@@ -1003,29 +1171,31 @@ class DolibarrSyncServiceTest extends TestCase
                 [FunctionEnum::DIRECTOR()->getValue(), ['gender' => 'X'], null, null, 'Directeur(ice)'],
                 [FunctionEnum::DIRECTOR()->getValue(), ['gender' => 'M'], null, null, 'Directeur'],
                 [FunctionEnum::DIRECTOR()->getValue(), ['gender' => 'F'], null, null, 'Directrice'],
+                [FunctionEnum::TEACHER()->getValue(), ['gender' => 'X'], null, null, 'Professeur(e)'],
+                [FunctionEnum::ARCHIVIST()->getValue(), ['gender' => 'X'], null, null, 'Archiviste'],
+                [FunctionEnum::TREASURER()->getValue(), ['gender' => 'X'], null, null, 'Trésorier(ère)'],
+                [FunctionEnum::ADMINISTRATIVE_STAFF()->getValue(), ['gender' => 'X'], null, null, 'Personnel administratif'],
             ]
         );
 
-        $syncService = $this->newDolibarrSyncService();
-
         $this->assertEquals(
             'Président(e)',
-            $syncService->formatContactPosition([FunctionEnum::PRESIDENT()->getValue()])
+            $dolibarrSyncService->formatContactPosition([FunctionEnum::PRESIDENT()->getValue()])
         );
 
         $this->assertEquals(
             'Président',
-            $syncService->formatContactPosition([FunctionEnum::PRESIDENT()->getValue()], 'MISTER')
+            $dolibarrSyncService->formatContactPosition([FunctionEnum::PRESIDENT()->getValue()], 'MISTER')
         );
 
         $this->assertEquals(
             'Présidente',
-            $syncService->formatContactPosition([FunctionEnum::PRESIDENT()->getValue()], 'MISS')
+            $dolibarrSyncService->formatContactPosition([FunctionEnum::PRESIDENT()->getValue()], 'MISS')
         );
 
         $this->assertEquals(
             'Présidente, Directrice',
-            $syncService->formatContactPosition(
+            $dolibarrSyncService->formatContactPosition(
                 [FunctionEnum::PRESIDENT()->getValue(), FunctionEnum::DIRECTOR()->getValue()],
                 'MISS'
             )
@@ -1033,19 +1203,48 @@ class DolibarrSyncServiceTest extends TestCase
 
         $this->assertEquals(
             'Président, Directeur',
-            $syncService->formatContactPosition(
+            $dolibarrSyncService->formatContactPosition(
                 [FunctionEnum::PRESIDENT()->getValue(), FunctionEnum::DIRECTOR()->getValue(), FunctionEnum::ADHERENT()->getValue()],
                 'MISTER'
             )
         );
+
+        $this->assertEquals(
+            'Président, Directeur',
+            $dolibarrSyncService->formatContactPosition(
+                [FunctionEnum::PRESIDENT()->getValue(), FunctionEnum::DIRECTOR()->getValue(), FunctionEnum::ADHERENT()->getValue()],
+                'MISTER'
+            )
+        );
+
+        $this->assertEquals(
+            'Président(e), Directeur(ice), Professeur(e), Archiviste, Trésorier(ère), Pers...',
+            $dolibarrSyncService->formatContactPosition(
+                [
+                    FunctionEnum::PRESIDENT()->getValue(),
+                    FunctionEnum::DIRECTOR()->getValue(),
+                    FunctionEnum::TEACHER()->getValue(),
+                    FunctionEnum::ARCHIVIST()->getValue(),
+                    FunctionEnum::TREASURER()->getValue(),
+                    FunctionEnum::ADMINISTRATIVE_STAFF()->getValue(),
+                ],
+                'X'
+            )
+        );
     }
 
     public function testFormatPhoneNumber() {
+        $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
+            ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
+                $this->dolibarrApiService, $this->addressPostalUtils, $this->arrayUtils, $this->translator, $this->logger])
+            ->setMethodsExcept(['formatPhoneNumber'])
+            ->getMock();
+
         $phoneUtil = PhoneNumberUtil::getInstance();
         $phoneNumber = $phoneUtil->parse('01 02 03 04 05', "FR");
         $this->assertEquals(
             '+33102030405',
-            TestableDolibarrSyncService::formatPhoneNumber($phoneNumber)
+            $dolibarrSyncService->formatPhoneNumber($phoneNumber)
         );
     }
 }