Browse Source

fix unit tests

Olivier Massot 1 year ago
parent
commit
46f9cbeac0

+ 1 - 1
src/Repository/Organization/OrganizationIdentificationRepository.php

@@ -12,7 +12,7 @@ use Doctrine\Persistence\ManagerRegistry;
  * @method OrganizationIdentification|null find($id, $lockMode = null, $lockVersion = null)
  * @method OrganizationIdentification|null findOneBy(array $criteria, array $orderBy = null)
  */
-final class OrganizationIdentificationRepository extends ServiceEntityRepository
+class OrganizationIdentificationRepository extends ServiceEntityRepository
 {
     public function __construct(ManagerRegistry $registry)
     {

+ 19 - 8
src/Service/Organization/OrganizationFactory.php

@@ -157,6 +157,7 @@ class OrganizationFactory
         // Création de l'organisation dans la base adminassos (géré par la V1)
         try {
             $this->updateAdminassosDb($organization);
+            $this->logger->info("Adminassos db updated");
         } catch (\Throwable $e) {
             $this->logger->critical("An error happened while updating the adminassos db, please proceed manually.");
             $this->logger->debug($e);
@@ -187,7 +188,7 @@ class OrganizationFactory
             )
         ) {
             throw new \RuntimeException(
-                "This siret number is already registered : '" . $organizationCreationRequest->getSiretNumber()
+                "This siret number is already registered : '" . $organizationCreationRequest->getSiretNumber() . "'"
             );
         }
 
@@ -198,7 +199,7 @@ class OrganizationFactory
             )
         ) {
             throw new \RuntimeException(
-                "This RNA identifier (waldec number) is already registered : '" . $organizationCreationRequest->getWaldecNumber()
+                "This RNA identifier (waldec number) is already registered : '" . $organizationCreationRequest->getWaldecNumber() . "'"
             );
         }
 
@@ -209,7 +210,7 @@ class OrganizationFactory
             )
         ) {
             throw new \RuntimeException(
-                "This CMF identifier is already registered : '" . $organizationCreationRequest->getIdentifier()
+                "This CMF identifier is already registered : '" . $organizationCreationRequest->getIdentifier() . "'"
             );
         }
 
@@ -328,6 +329,10 @@ class OrganizationFactory
             $presidentCreationRequest->setAuthorId($organizationCreationRequest->getAuthorId());
 
             $presidentAccess = $this->makeAccess($presidentCreationRequest);
+
+            $presidentAccess->setCreateDate($organizationCreationRequest->getCreationDate());
+            $presidentAccess->setCreatedBy($organizationCreationRequest->getAuthorId());
+
             $organization->addAccess($presidentAccess);
             $this->logger->debug(" - President access created");
         }
@@ -339,6 +344,10 @@ class OrganizationFactory
             $directorCreationRequest->setAuthorId($organizationCreationRequest->getAuthorId());
 
             $directorAccess = $this->makeAccess($directorCreationRequest);
+
+            $directorAccess->setCreateDate($organizationCreationRequest->getCreationDate());
+            $directorAccess->setCreatedBy($organizationCreationRequest->getAuthorId());
+
             $organization->addAccess($directorAccess);
             $this->logger->debug(" - Director access created");
         }
@@ -618,8 +627,6 @@ class OrganizationFactory
 
         $access = new Access();
         $access->setPerson($person);
-        $access->setCreateDate($creationRequestData->getCreationDate());
-        $access->setCreatedBy($creationRequestData->getAuthorId());
 
         return $access;
     }
@@ -721,10 +728,14 @@ class OrganizationFactory
 
     protected function updateAdminassosDb(Organization $organization): void
     {
-        $this->apiLegacyRequestService->post(
+        $response = $this->apiLegacyRequestService->post(
             '/_internal/secure/organization/creation-event',
             [ 'organizationId' => $organization->getId() ]
         );
+
+        if ($response->getStatusCode() !== Response::HTTP_OK) {
+            throw new \RuntimeException('An error happened while updating the adminassos database');
+        }
     }
 
     /**
@@ -733,9 +744,9 @@ class OrganizationFactory
      * @øee sql/schema-extensions/003-view_organization_identification.sql
      *
      * @param string $value
-     * @return array|string|string[]|null
+     * @return string
      */
-    protected function normalizeIdentificationField(string $value) {
+    protected function normalizeIdentificationField(string $value): string {
         $value = strtolower(trim($value));
         $value = preg_replace('/[éèê]/', 'e', $value);
         $value = preg_replace('/[à]/', 'a', $value);

+ 5 - 1
src/Service/Typo3/Typo3Service.php

@@ -26,7 +26,11 @@ class Typo3Service
      */
     protected function sendCommand(string $route, array $parameters): ResponseInterface
     {
-        $url = UrlBuilder::concatParameters('/typo3' . $route, $parameters);
+        $url = UrlBuilder::concat(
+            '/typo3',
+            [$route],
+            $parameters
+        );
         return $this->typo3_client->request('GET', $url);
     }
 

+ 1 - 2
src/Service/Utils/UrlBuilder.php

@@ -25,9 +25,8 @@ class UrlBuilder
     {
         $url = $base;
         foreach ($tails as $tail) {
-            $url = trim($url, '/').'/'.trim(strval($tail), '/');
+            $url = rtrim($url, '/').'/'.ltrim(strval($tail), '/');
         }
-
         return $url;
     }
 

+ 313 - 93
tests/Unit/Service/Organization/OrganizationFactoryTest.php

@@ -29,8 +29,10 @@ use App\Enum\Organization\SettingsProductEnum;
 use App\Enum\Person\AddressPostalPersonTypeEnum;
 use App\Enum\Person\GenderEnum;
 use App\Repository\Core\CountryRepository;
+use App\Repository\Organization\OrganizationIdentificationRepository;
 use App\Repository\Organization\OrganizationRepository;
 use App\Repository\Person\PersonRepository;
+use App\Service\ApiLegacy\ApiLegacyRequestService;
 use App\Service\Dolibarr\DolibarrApiService;
 use App\Service\Organization\OrganizationFactory;
 use App\Service\Organization\Utils as OrganizationUtils;
@@ -46,9 +48,9 @@ use Symfony\Component\HttpFoundation\Response;
 use Symfony\Contracts\HttpClient\ResponseInterface;
 
 class TestableOrganizationFactory extends OrganizationFactory {
-    public function isExistingOrganization(OrganizationCreationRequest $organizationCreationRequest): bool
+    public function interruptIfOrganizationExists(OrganizationCreationRequest $organizationCreationRequest): void
     {
-        return parent::isExistingOrganization($organizationCreationRequest);
+        parent::interruptIfOrganizationExists($organizationCreationRequest);
     }
 
     public function validateSubdomain(string $subdomainValue): void
@@ -113,20 +115,32 @@ class TestableOrganizationFactory extends OrganizationFactory {
     public function createTypo3Website(Organization $organization): ?int {
         return parent::createTypo3Website($organization);
     }
+
+    public function updateAdminassosDb(Organization $organization): void
+    {
+        parent::updateAdminassosDb($organization);
+    }
+
+    public function normalizeIdentificationField(string $value): string
+    {
+        return parent::normalizeIdentificationField($value);
+    }
 }
 
 class OrganizationFactoryTest extends TestCase
 {
-    private readonly MockObject | SubdomainService       $subdomainService;
-    private readonly MockObject | OrganizationRepository $organizationRepository;
-    private readonly MockObject | CountryRepository      $countryRepository;
-    private readonly MockObject | OrganizationUtils      $organizationUtils;
-    private readonly MockObject | Typo3Service           $typo3Service;
-    private readonly MockObject | DolibarrApiService     $dolibarrApiService;
-    private readonly MockObject | EntityManagerInterface $entityManager;
-    private readonly MockObject | PersonRepository       $personRepository;
-    private readonly MockObject | BindFileService        $bindFileService;
-    private readonly MockObject | LoggerInterface        $logger;
+    private readonly MockObject | SubdomainService                            $subdomainService;
+    private readonly MockObject | OrganizationRepository                      $organizationRepository;
+    private readonly MockObject | CountryRepository                           $countryRepository;
+    private readonly MockObject | OrganizationUtils                           $organizationUtils;
+    private readonly MockObject | Typo3Service                                $typo3Service;
+    private readonly MockObject | DolibarrApiService                          $dolibarrApiService;
+    private readonly MockObject | EntityManagerInterface                      $entityManager;
+    private readonly MockObject | PersonRepository                            $personRepository;
+    private readonly MockObject | BindFileService                             $bindFileService;
+    private readonly MockObject | LoggerInterface                             $logger;
+    private readonly MockObject | OrganizationIdentificationRepository        $organizationIdentificationRepository;
+    private readonly MockObject | ApiLegacyRequestService                     $apiLegacyRequestService;
 
     public function setUp(): void
     {
@@ -140,6 +154,8 @@ class OrganizationFactoryTest extends TestCase
         $this->personRepository = $this->getMockBuilder(PersonRepository::class)->disableOriginalConstructor()->getMock();
         $this->bindFileService = $this->getMockBuilder(BindFileService::class)->disableOriginalConstructor()->getMock();
         $this->logger = $this->getMockBuilder(LoggerInterface::class)->disableOriginalConstructor()->getMock();
+        $this->organizationIdentificationRepository = $this->getMockBuilder(OrganizationIdentificationRepository::class)->disableOriginalConstructor()->getMock();
+        $this->apiLegacyRequestService = $this->getMockBuilder(ApiLegacyRequestService::class)->disableOriginalConstructor()->getMock();
     }
 
     public function tearDown(): void
@@ -161,7 +177,9 @@ class OrganizationFactoryTest extends TestCase
                     $this->dolibarrApiService,
                     $this->entityManager,
                     $this->personRepository,
-                    $this->bindFileService
+                    $this->bindFileService,
+                    $this->organizationIdentificationRepository,
+                    $this->apiLegacyRequestService
                 ])
             ->setMethodsExcept(['setLoggerInterface', $methodName])
             ->getMock();
@@ -182,7 +200,7 @@ class OrganizationFactoryTest extends TestCase
 
         $this->entityManager->expects(self::once())->method('beginTransaction');
 
-        $organizationFactory->expects(self::once())->method('isExistingOrganization')->willReturn(false);
+        $organizationFactory->expects(self::once())->method('interruptIfOrganizationExists');
         $organizationFactory->expects(self::once())->method('validateSubdomain')->with('subdomain');
 
         $organization = $this->getMockBuilder(Organization::class)->getMock();
@@ -210,7 +228,8 @@ class OrganizationFactoryTest extends TestCase
         $organizationFactory
             ->expects(self::once())
             ->method('createTypo3Website')
-            ->with($organization);
+            ->with($organization)
+            ->willReturn(789);
 
         $this->logger
             ->method('info')
@@ -218,9 +237,11 @@ class OrganizationFactoryTest extends TestCase
                 ["Start the creation of a new organization named 'foo'"],
                 ["Subdomain is valid and available : 'subdomain'"],
                 ["Organization created with all its relations"],
-                ["New dolibarr structure created (uid : 456)"],
                 ["Organization persisted in the DB"],
+                ["New dolibarr structure created (uid : 456)"],
                 ["Subdomain registered"],
+                ["Typo3 website created (root uid: 789)"],
+                ["Adminassos db updated"]
             );
 
         $result = $organizationFactory->create($organizationCreationRequest);
@@ -242,7 +263,7 @@ class OrganizationFactoryTest extends TestCase
 
         $this->entityManager->expects(self::once())->method('beginTransaction');
 
-        $organizationFactory->expects(self::once())->method('isExistingOrganization')->willReturn(false);
+        $organizationFactory->expects(self::once())->method('interruptIfOrganizationExists');
         $organizationFactory->expects(self::once())->method('validateSubdomain')->with('subdomain');
 
         $organization = $this->getMockBuilder(Organization::class)->getMock();
@@ -295,7 +316,7 @@ class OrganizationFactoryTest extends TestCase
 
         $this->entityManager->expects(self::once())->method('beginTransaction');
 
-        $organizationFactory->expects(self::once())->method('isExistingOrganization')->willReturn(true);
+        $organizationFactory->expects(self::once())->method('interruptIfOrganizationExists')->willThrowException(new \RuntimeException('An organization named foo already exists'));
 
         $organizationFactory->expects(self::never())->method('validateSubdomain');
 
@@ -346,7 +367,7 @@ class OrganizationFactoryTest extends TestCase
         $organizationCreationRequest->method('isClient')->willReturn(true);
         $organizationCreationRequest->method('getCreateWebsite')->willReturn(false);
 
-        $organizationFactory->method('isExistingOrganization')->willReturn(false);
+        $organizationFactory->expects(self::once())->method('interruptIfOrganizationExists');
 
         $organization = $this->getMockBuilder(Organization::class)->getMock();
         $organizationFactory
@@ -377,23 +398,270 @@ class OrganizationFactoryTest extends TestCase
         );
     }
 
-    public function testIsExistingOrganization(): void
+    public function testInterruptIfOrganizationExistsNotExisting(): void
+    {
+        $organizationFactory = $this->getOrganizationFactoryMockFor('interruptIfOrganizationExists');
+
+        $organizationCreationRequest = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
+        $organizationCreationRequest->method('getName')->willReturn('foo');
+        $organizationCreationRequest->method('getSiretNumber')->willReturn('');
+        $organizationCreationRequest->method('getWaldecNumber')->willReturn('');
+        $organizationCreationRequest->method('getIdentifier')->willReturn('');
+        $organizationCreationRequest->method('getStreetAddress1')->willReturn('part1');
+        $organizationCreationRequest->method('getStreetAddress2')->willReturn('part2');
+        $organizationCreationRequest->method('getStreetAddress3')->willReturn('part3');
+        $organizationCreationRequest->method('getCity')->willReturn('Paris');
+        $organizationCreationRequest->method('getPostalCode')->willReturn('75000');
+
+        $this->organizationIdentificationRepository
+            ->expects(self::exactly(2))
+            ->method('findOneBy')
+            ->willReturn(null);
+
+        $organizationFactory
+            ->method('normalizeIdentificationField')
+            ->willReturnMap([
+                ['foo', 'foo'],
+                ['part1 part2 part3', 'part1 part2 part3']
+            ]);
+
+        $organizationFactory->interruptIfOrganizationExists($organizationCreationRequest);
+    }
+
+    public function testInterruptIfOrganizationExistsNotExistingNonExistingWithIdentifiers(): void
+    {
+        $organizationFactory = $this->getOrganizationFactoryMockFor('interruptIfOrganizationExists');
+
+        $organizationCreationRequest = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
+        $organizationCreationRequest->method('getName')->willReturn('foo');
+        $organizationCreationRequest->method('getSiretNumber')->willReturn('123456');
+        $organizationCreationRequest->method('getWaldecNumber')->willReturn('W123456');
+        $organizationCreationRequest->method('getIdentifier')->willReturn('FR000000000000');
+        $organizationCreationRequest->method('getStreetAddress1')->willReturn('part1');
+        $organizationCreationRequest->method('getStreetAddress2')->willReturn('part2');
+        $organizationCreationRequest->method('getStreetAddress3')->willReturn('part3');
+        $organizationCreationRequest->method('getCity')->willReturn('Paris');
+        $organizationCreationRequest->method('getPostalCode')->willReturn('75000');
+
+        $organizationFactory
+            ->method('normalizeIdentificationField')
+            ->willReturnMap([
+                ['foo', 'foo'],
+                ['part1 part2 part3', 'part1 part2 part3']
+            ]);
+
+        $this->organizationIdentificationRepository
+            ->expects(self::exactly(5))
+            ->method('findOneBy')
+            ->willReturnMap([
+                [['siretNumber' => '123456'], null, null],
+                [['waldecNumber' => 'W123456'], null, null],
+                [['identifier' => 'FR000000000000'], null, null],
+                [['normalizedName' => 'foo', 'addressCity' => 'Paris'], null, null],
+                [['normalizedAddress' => 'part1 part2 part3', 'addressCity' => 'Paris', 'postalCode' => '75000'], null, null],
+            ]);
+
+        $organizationFactory->interruptIfOrganizationExists($organizationCreationRequest);
+    }
+
+    public function testInterruptIfOrganizationExistsExistingWithSiret(): void
+    {
+        $organizationFactory = $this->getOrganizationFactoryMockFor('interruptIfOrganizationExists');
+
+        $organizationCreationRequest = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
+        $organizationCreationRequest->method('getName')->willReturn('foo');
+        $organizationCreationRequest->method('getSiretNumber')->willReturn('123456');
+        $organizationCreationRequest->method('getWaldecNumber')->willReturn('W123456');
+        $organizationCreationRequest->method('getIdentifier')->willReturn('FR000000000000');
+        $organizationCreationRequest->method('getStreetAddress1')->willReturn('part1');
+        $organizationCreationRequest->method('getStreetAddress2')->willReturn('part2');
+        $organizationCreationRequest->method('getStreetAddress3')->willReturn('part3');
+        $organizationCreationRequest->method('getCity')->willReturn('Paris');
+        $organizationCreationRequest->method('getPostalCode')->willReturn('75000');
+
+        $organizationFactory
+            ->method('normalizeIdentificationField')
+            ->willReturnMap([
+                ['foo', 'foo'],
+                ['part1 part2 part3', 'part1 part2 part3']
+            ]);
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+
+        $this->organizationIdentificationRepository
+            ->method('findOneBy')
+            ->willReturnMap([
+                [['siretNumber' => '123456'], null, $organization],
+                [['waldecNumber' => 'W123456'], null, null],
+                [['identifier' => 'FR000000000000'], null, null],
+                [['normalizedName' => 'foo', 'addressCity' => 'Paris'], null, null],
+                [['normalizedAddress' => 'part1 part2 part3', 'addressCity' => 'Paris', 'postalCode' => '75000'], null, null],
+            ]);
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage("This siret number is already registered : '123456'");
+
+        $organizationFactory->interruptIfOrganizationExists($organizationCreationRequest);
+    }
+
+    public function testInterruptIfOrganizationExistsExistingWithWaldec(): void
     {
-        $organizationFactory = $this->getOrganizationFactoryMockFor('isExistingOrganization');
+        $organizationFactory = $this->getOrganizationFactoryMockFor('interruptIfOrganizationExists');
+
+        $organizationCreationRequest = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
+        $organizationCreationRequest->method('getName')->willReturn('foo');
+        $organizationCreationRequest->method('getSiretNumber')->willReturn('123456');
+        $organizationCreationRequest->method('getWaldecNumber')->willReturn('W123456');
+        $organizationCreationRequest->method('getIdentifier')->willReturn('FR000000000000');
+        $organizationCreationRequest->method('getStreetAddress1')->willReturn('part1');
+        $organizationCreationRequest->method('getStreetAddress2')->willReturn('part2');
+        $organizationCreationRequest->method('getStreetAddress3')->willReturn('part3');
+        $organizationCreationRequest->method('getCity')->willReturn('Paris');
+        $organizationCreationRequest->method('getPostalCode')->willReturn('75000');
+
+        $organizationFactory
+            ->method('normalizeIdentificationField')
+            ->willReturnMap([
+                ['foo', 'foo'],
+                ['part1 part2 part3', 'part1 part2 part3']
+            ]);
 
-        $organizationCreationRequest1 = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
-        $organizationCreationRequest1->method('getName')->willReturn('foo');
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
 
-        $organizationCreationRequest2 = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
-        $organizationCreationRequest2->method('getName')->willReturn('bar');
+        $this->organizationIdentificationRepository
+            ->method('findOneBy')
+            ->willReturnMap([
+                [['siretNumber' => '123456'], null, null],
+                [['waldecNumber' => 'W123456'], null, $organization],
+                [['identifier' => 'FR000000000000'], null, null],
+                [['normalizedName' => 'foo', 'addressCity' => 'Paris'], null, null],
+                [['normalizedAddress' => 'part1 part2 part3', 'addressCity' => 'Paris', 'postalCode' => '75000'], null, null],
+            ]);
 
-        $this->organizationRepository->method('count')->willReturnMap([
-            [['name' => 'foo'], 1],
-            [['name' => 'bar'], 0],
-        ]);
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage("This RNA identifier (waldec number) is already registered : 'W123456'");
 
-        $this->assertTrue($organizationFactory->isExistingOrganization($organizationCreationRequest1));
-        $this->assertFalse($organizationFactory->isExistingOrganization($organizationCreationRequest2));
+        $organizationFactory->interruptIfOrganizationExists($organizationCreationRequest);
+    }
+
+    public function testInterruptIfOrganizationExistsExistingWithCMFIdentifier(): void
+    {
+        $organizationFactory = $this->getOrganizationFactoryMockFor('interruptIfOrganizationExists');
+
+        $organizationCreationRequest = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
+        $organizationCreationRequest->method('getName')->willReturn('foo');
+        $organizationCreationRequest->method('getSiretNumber')->willReturn('123456');
+        $organizationCreationRequest->method('getWaldecNumber')->willReturn('W123456');
+        $organizationCreationRequest->method('getIdentifier')->willReturn('FR000000000000');
+        $organizationCreationRequest->method('getStreetAddress1')->willReturn('part1');
+        $organizationCreationRequest->method('getStreetAddress2')->willReturn('part2');
+        $organizationCreationRequest->method('getStreetAddress3')->willReturn('part3');
+        $organizationCreationRequest->method('getCity')->willReturn('Paris');
+        $organizationCreationRequest->method('getPostalCode')->willReturn('75000');
+
+        $organizationFactory
+            ->method('normalizeIdentificationField')
+            ->willReturnMap([
+                ['foo', 'foo'],
+                ['part1 part2 part3', 'part1 part2 part3']
+            ]);
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+
+        $this->organizationIdentificationRepository
+            ->method('findOneBy')
+            ->willReturnMap([
+                [['siretNumber' => '123456'], null, null],
+                [['waldecNumber' => 'W123456'], null, null],
+                [['identifier' => 'FR000000000000'], null, $organization],
+                [['normalizedName' => 'foo', 'addressCity' => 'Paris'], null, null],
+                [['normalizedAddress' => 'part1 part2 part3', 'addressCity' => 'Paris', 'postalCode' => '75000'], null, null],
+            ]);
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage("This CMF identifier is already registered : 'FR000000000000'");
+
+        $organizationFactory->interruptIfOrganizationExists($organizationCreationRequest);
+    }
+
+    public function testInterruptIfOrganizationExistsExistingWithSameNameAndCity(): void
+    {
+        $organizationFactory = $this->getOrganizationFactoryMockFor('interruptIfOrganizationExists');
+
+        $organizationCreationRequest = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
+        $organizationCreationRequest->method('getName')->willReturn('foo');
+        $organizationCreationRequest->method('getSiretNumber')->willReturn('123456');
+        $organizationCreationRequest->method('getWaldecNumber')->willReturn('W123456');
+        $organizationCreationRequest->method('getIdentifier')->willReturn('FR000000000000');
+        $organizationCreationRequest->method('getStreetAddress1')->willReturn('part1');
+        $organizationCreationRequest->method('getStreetAddress2')->willReturn('part2');
+        $organizationCreationRequest->method('getStreetAddress3')->willReturn('part3');
+        $organizationCreationRequest->method('getCity')->willReturn('Paris');
+        $organizationCreationRequest->method('getPostalCode')->willReturn('75000');
+
+        $organizationFactory
+            ->method('normalizeIdentificationField')
+            ->willReturnMap([
+                ['foo', 'foo'],
+                ['part1 part2 part3', 'part1 part2 part3']
+            ]);
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+
+        $this->organizationIdentificationRepository
+            ->method('findOneBy')
+            ->willReturnMap([
+                [['siretNumber' => '123456'], null, null],
+                [['waldecNumber' => 'W123456'], null, null],
+                [['identifier' => 'FR000000000000'], null, null],
+                [['normalizedName' => 'foo', 'addressCity' => 'Paris'], null, $organization],
+                [['normalizedAddress' => 'part1 part2 part3', 'addressCity' => 'Paris', 'postalCode' => '75000'], null, null],
+            ]);
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage("An organization named 'foo' already exists in Paris");
+
+        $organizationFactory->interruptIfOrganizationExists($organizationCreationRequest);
+    }
+
+    public function testInterruptIfOrganizationExistsExistingWithSameAddress(): void
+    {
+        $organizationFactory = $this->getOrganizationFactoryMockFor('interruptIfOrganizationExists');
+
+        $organizationCreationRequest = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
+        $organizationCreationRequest->method('getName')->willReturn('foo');
+        $organizationCreationRequest->method('getSiretNumber')->willReturn('123456');
+        $organizationCreationRequest->method('getWaldecNumber')->willReturn('W123456');
+        $organizationCreationRequest->method('getIdentifier')->willReturn('FR000000000000');
+        $organizationCreationRequest->method('getStreetAddress1')->willReturn('part1');
+        $organizationCreationRequest->method('getStreetAddress2')->willReturn('part2');
+        $organizationCreationRequest->method('getStreetAddress3')->willReturn('part3');
+        $organizationCreationRequest->method('getCity')->willReturn('Paris');
+        $organizationCreationRequest->method('getPostalCode')->willReturn('75000');
+
+        $organizationFactory
+            ->method('normalizeIdentificationField')
+            ->willReturnMap([
+                ['foo', 'foo'],
+                ['part1 part2 part3', 'part1 part2 part3']
+            ]);
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+
+        $this->organizationIdentificationRepository
+            ->method('findOneBy')
+            ->willReturnMap([
+                [['siretNumber' => '123456'], null, null],
+                [['waldecNumber' => 'W123456'], null, null],
+                [['identifier' => 'FR000000000000'], null, null],
+                [['normalizedName' => 'foo', 'addressCity' => 'Paris'], null, null],
+                [['normalizedAddress' => 'part1 part2 part3', 'addressCity' => 'Paris', 'postalCode' => '75000'], null, $organization],
+            ]);
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage("An organization already exists at this address.");
+
+        $organizationFactory->interruptIfOrganizationExists($organizationCreationRequest);
     }
 
     public function testValidateSubdomain(): void {
@@ -562,7 +830,7 @@ class OrganizationFactoryTest extends TestCase
             ->with($organizationCreationRequest)
             ->willReturn($subdomain);
 
-        $subdomain->expects(self::once())->method('setOrganization')->with($organization);
+        $organization->expects(self::once())->method('addSubdomain')->with($subdomain);
 
         // Enregistrement du sous domaine dans Parameters (retrocompatibilité v1)
         $organization->method('getParameters')->willReturn($parameters);
@@ -570,7 +838,6 @@ class OrganizationFactoryTest extends TestCase
         $organizationCreationRequest->method('getSubdomain')->willReturn('foo');
         $parameters->expects(self::once())->method('setSubDomain')->with('foo');
         $parameters->expects(self::once())->method('setOtherWebsite')->with('https://foo.opentalent.fr');
-        $this->entityManager->expects(self::once())->method('persist')->with($parameters);
 
         $result = $organizationFactory->makeOrganizationWithRelations($organizationCreationRequest);
 
@@ -614,8 +881,6 @@ class OrganizationFactoryTest extends TestCase
         $organizationCreationRequest->method('getLegalStatus')->willReturn(LegalEnum::ASSOCIATION_LAW_1901);
         $organizationCreationRequest->method('getPrincipalType')->willReturn(PrincipalTypeEnum::ARTISTIC_EDUCATION_ONLY);
 
-        $this->entityManager->expects(self::once())->method('persist')->with($this->isInstanceOf(Organization::class));
-
         $organization = $organizationFactory->makeOrganization($organizationCreationRequest);
 
         $this->assertEquals(
@@ -640,8 +905,6 @@ class OrganizationFactoryTest extends TestCase
 
         $organizationCreationRequest = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
 
-        $this->entityManager->expects(self::once())->method('persist')->with($this->isInstanceOf(Parameters::class));
-
         $parameters = $organizationFactory->makeParameters($organizationCreationRequest);
 
         $this->assertInstanceOf(Parameters::class, $parameters);
@@ -655,8 +918,6 @@ class OrganizationFactoryTest extends TestCase
 
         $organizationCreationRequest->method('getProduct')->willReturn(SettingsProductEnum::ARTIST_PREMIUM);
 
-        $this->entityManager->expects(self::once())->method('persist')->with($this->isInstanceOf(Settings::class));
-
         $settings = $organizationFactory->makeSettings($organizationCreationRequest);
 
         $this->assertEquals(
@@ -681,15 +942,6 @@ class OrganizationFactoryTest extends TestCase
 
         $this->countryRepository->expects(self::once())->method('find')->with(1)->willReturn($country);
 
-        $this
-            ->entityManager
-            ->expects(self::exactly(2))
-            ->method('persist')
-            ->withConsecutive(
-                [$this->isInstanceOf(AddressPostal::class)],
-                [$this->isInstanceOf(OrganizationAddressPostal::class)]
-            );
-
         $organizationAddressPostal = $organizationFactory->makePostalAddress($organizationCreationRequest);
 
         $this->assertEquals(
@@ -739,8 +991,6 @@ class OrganizationFactoryTest extends TestCase
         $organizationCreationRequest->method('getPhoneNumber')->willReturn('+33102030405');
         $organizationCreationRequest->method('getEmail')->willReturn('contact@domain.net');
 
-        $this->entityManager->expects(self::once())->method('persist')->with($this->isInstanceOf(ContactPoint::class));
-
         $contactPoint = $organizationFactory->makeContactPoint($organizationCreationRequest);
 
         $this->assertEquals(
@@ -767,9 +1017,10 @@ class OrganizationFactoryTest extends TestCase
         $organizationCreationRequest = $this->getMockBuilder(OrganizationCreationRequest::class)->getMock();
         $organizationCreationRequest->method('getParentId')->willReturn(123);
 
-        $this->entityManager->expects(self::once())->method('persist')->with($this->isInstanceOf(NetworkOrganization::class));
-
         $parent = $this->getMockBuilder(Organization::class)->getMock();
+        $settings = $this->getMockBuilder(Settings::class)->getMock();
+        $settings->method('getProduct')->willReturn(SettingsProductEnum::MANAGER);
+        $parent->method('getSettings')->willReturn($settings);
         $this->organizationRepository->expects(self::once())->method('find')->with(123)->willReturn($parent);
 
         $network = $this->getMockBuilder(Network::class)->getMock();
@@ -820,6 +1071,9 @@ class OrganizationFactoryTest extends TestCase
         $organizationCreationRequest->method('getParentId')->willReturn(123);
 
         $parent = $this->getMockBuilder(Organization::class)->getMock();
+        $settings = $this->getMockBuilder(Settings::class)->getMock();
+        $settings->method('getProduct')->willReturn(SettingsProductEnum::MANAGER);
+        $parent->method('getSettings')->willReturn($settings);
         $this->organizationRepository->expects(self::once())->method('find')->with(123)->willReturn($parent);
 
         $this->organizationUtils
@@ -844,6 +1098,9 @@ class OrganizationFactoryTest extends TestCase
         $organizationCreationRequest->method('getIdentifier')->willReturn('invalid');
 
         $parent = $this->getMockBuilder(Organization::class)->getMock();
+        $settings = $this->getMockBuilder(Settings::class)->getMock();
+        $settings->method('getProduct')->willReturn(SettingsProductEnum::MANAGER);
+        $parent->method('getSettings')->willReturn($settings);
         $this->organizationRepository->method('find')->with(123)->willReturn($parent);
 
         $network = $this->getMockBuilder(Network::class)->getMock();
@@ -871,6 +1128,9 @@ class OrganizationFactoryTest extends TestCase
         $organizationCreationRequest->method('getIdentifier')->willReturn('invalid');
 
         $parent = $this->getMockBuilder(Organization::class)->getMock();
+        $settings = $this->getMockBuilder(Settings::class)->getMock();
+        $settings->method('getProduct')->willReturn(SettingsProductEnum::MANAGER);
+        $parent->method('getSettings')->willReturn($settings);
         $this->organizationRepository->method('find')->with(123)->willReturn($parent);
 
         $network = $this->getMockBuilder(Network::class)->getMock();
@@ -900,15 +1160,6 @@ class OrganizationFactoryTest extends TestCase
 
         $organizationCreationRequest->method('getSubdomain')->willReturn('foo');
 
-        $this
-            ->entityManager
-            ->expects(self::exactly(2))
-            ->method('persist')
-            ->withConsecutive(
-                [$this->isInstanceOf(Person::class)],
-                [$this->isInstanceOf(Access::class)]
-            );
-
         $adminAccess = $organizationFactory->makeAdminAccess($organizationCreationRequest);
 
         $this->assertTrue(
@@ -987,14 +1238,6 @@ class OrganizationFactoryTest extends TestCase
                 ->with($organizationMemberCreationRequest)
                 ->willReturn($contactPoint);
 
-        $this->entityManager
-            ->expects(self::exactly(2))
-            ->method('persist')
-            ->withConsecutive(
-                [$this->isInstanceOf(Person::class)],
-                [$this->isInstanceOf(Access::class)]
-            );
-
         $access = $organizationFactory->makeAccess($organizationMemberCreationRequest);
 
         $this->assertInstanceOf(Access::class, $access);
@@ -1035,11 +1278,6 @@ class OrganizationFactoryTest extends TestCase
             ->with(123)
             ->willReturn($person);
 
-        $this->entityManager
-            ->expects(self::once())
-            ->method('persist')
-            ->with($this->isInstanceOf(Access::class));
-
         $access = $organizationFactory->makeAccess(123);
 
         $this->assertInstanceOf(Access::class, $access);
@@ -1069,14 +1307,6 @@ class OrganizationFactoryTest extends TestCase
             ->with(123)
             ->willReturn($country);
 
-        $this->entityManager
-            ->expects(self::exactly(2))
-            ->method('persist')
-            ->withConsecutive(
-                [$this->isInstanceOf(AddressPostal::class)],
-                [$this->isInstanceOf(PersonAddressPostal::class)]
-            );
-
         $personPostalAddress = $organizationFactory->makePersonPostalAddress($organizationMemberCreationRequest);
 
         $this->assertEquals(
@@ -1103,11 +1333,6 @@ class OrganizationFactoryTest extends TestCase
         $organizationMemberCreationRequest->method('getEmail')->willReturn('email@domain.com');
         $organizationMemberCreationRequest->method('getMobile')->willReturn('+33607080910');
 
-        $this->entityManager
-            ->expects(self::once())
-            ->method('persist')
-            ->with($this->isInstanceOf(ContactPoint::class));
-
         $contactPoint = $organizationFactory->makePersonContactPoint($organizationMemberCreationRequest);
 
         $this->assertEquals(
@@ -1163,11 +1388,6 @@ class OrganizationFactoryTest extends TestCase
 
         $organizationCreationRequest->method('getSubdomain')->willReturn('subdomain');
 
-        $this->entityManager
-            ->expects(self::once())
-            ->method('persist')
-            ->with($this->isInstanceOf(Subdomain::class));
-
         $subdomain = $organizationFactory->makeSubdomain($organizationCreationRequest);
 
         $this->assertEquals(
@@ -1188,7 +1408,7 @@ class OrganizationFactoryTest extends TestCase
 
         $response = $this->getMockBuilder(ResponseInterface::class)->disableOriginalConstructor()->getMock();
         $response->method('getStatusCode')->willReturn(Response::HTTP_OK);
-        $response->method('getContent')->willReturn('456');
+        $response->method('getContent')->willReturn('{"root_uid": 456}');
 
         $this->typo3Service->expects(self::once())->method('createSite')->with(123)->willReturn($response);
 

+ 1 - 1
tests/Unit/Service/Typo3/Typo3ServiceTest.php

@@ -36,7 +36,7 @@ class Typo3ServiceTest extends TestCase
         $this->typo3Client
             ->expects(self::once())
             ->method('request')
-            ->with('GET', '/typo3/index.php?route=foo&param=bar')
+            ->with('GET', '/typo3/foo?param=bar')
             ->willReturn($response);
 
         $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)

+ 6 - 2
tests/Unit/Service/Utils/UrlBuilderTest.php

@@ -23,8 +23,12 @@ class UrlBuilderTest extends TestCase
             UrlBuilder::concatPath('https://domain.org/', ['/abc/def'])
         );
         $this->assertEquals(
-            'https://domain.org/abc/def/ghi/jkl',
-            UrlBuilder::concatPath('https://domain.org/', ['/abc/def', 'ghi', '//jkl'])
+            'https://domain.org/abc/def/ghi/jkl/',
+            UrlBuilder::concatPath('https://domain.org/', ['/abc/def', 'ghi', '//jkl/'])
+        );
+        $this->assertEquals(
+            '/abc/def',
+            UrlBuilder::concatPath('/abc/', ['/def'])
         );
         $this->assertEquals(
             'https://domain.org/',