Quellcode durchsuchen

complete tests on services and minor fixes in src

Olivier Massot vor 3 Jahren
Ursprung
Commit
397863f4b6
75 geänderte Dateien mit 1955 neuen und 262 gelöschten Zeilen
  1. 0 1
      config/services.yaml
  2. 1 1
      src/Doctrine/Access/AccessExtensionInterface.php
  3. 1 1
      src/Doctrine/Access/Extensions/DateTimeConstraintExtension.php
  4. 1 1
      src/Entity/Core/File.php
  5. 1 0
      src/Enum/Education/AdvancedEducationNotationTypeEnum.php
  6. 3 2
      src/Service/Cotisation/Utils.php
  7. 6 6
      src/Service/Export/BaseExporter.php
  8. 42 14
      src/Service/Export/Model/LicenceCmf.php
  9. 1 1
      src/Service/Export/Model/LicenceCmfCollection.php
  10. 4 1
      src/Service/Mobyt/MobytUserStatusCreator.php
  11. 6 3
      src/Service/Organization/Utils.php
  12. 23 12
      src/Service/Security/Module.php
  13. 2 1
      src/Service/ServiceIterator/CurrentAccessExtensionIterator.php
  14. 35 3
      src/Service/Utils/DatesUtils.php
  15. 6 4
      src/Service/Utils/EntityUtils.php
  16. 2 2
      src/Service/Utils/Parser.php
  17. 21 15
      src/Service/Utils/Reflection.php
  18. 2 9
      src/Service/Utils/Uuid.php
  19. 4 2
      tests/Service/Access/AccessProfileCreatorTest.php
  20. 5 3
      tests/Service/Access/AdminAccessUtilsTest.php
  21. 18 2
      tests/Service/Access/OptionalsRoles/CriteriaNotationOptionalRoleTest.php
  22. 1 1
      tests/Service/Access/UtilsTest.php
  23. 20 2
      tests/Service/ApiLegacy/ApiLegacyRequestServiceTest.php
  24. 6 3
      tests/Service/Constraint/AbstractTimeConstraintsUtilsTest.php
  25. 31 1
      tests/Service/Constraint/ActivityYearConstraintTest.php
  26. 18 0
      tests/Service/Constraint/DateTimeConstraintTest.php
  27. 19 3
      tests/Service/Core/AddressPostalUtilsTest.php
  28. 1 1
      tests/Service/Core/ContactPointUtilsTest.php
  29. 5 1
      tests/Service/Cotisation/CotisationCreatorTest.php
  30. 9 22
      tests/Service/Cotisation/UtilsTest.php
  31. 23 3
      tests/Service/Dolibarr/DolibarrAccountCreatorTest.php
  32. 4 2
      tests/Service/Dolibarr/DolibarrApiServiceTest.php
  33. 80 2
      tests/Service/Dolibarr/DolibarrSyncServiceTest.php
  34. 7 1
      tests/Service/Education/EducationNotationUtilsTest.php
  35. 5 1
      tests/Service/Elasticsearch/EducationNotationUpdaterTest.php
  36. 181 9
      tests/Service/Export/BaseExporterTest.php
  37. 11 0
      tests/Service/Export/Encoder/PdfEncoderTest.php
  38. 18 1
      tests/Service/Export/LicenceCmfExporterTest.php
  39. 32 0
      tests/Service/Export/Model/LicenceCmfCollectionTest.php
  40. 75 0
      tests/Service/Export/Model/LicenceCmfTest.php
  41. 17 2
      tests/Service/MailHubTest.php
  42. 24 6
      tests/Service/MercureHubTest.php
  43. 3 2
      tests/Service/Mobyt/MobytServiceTest.php
  44. 80 5
      tests/Service/Mobyt/MobytUserStatusCreatorTest.php
  45. 18 0
      tests/Service/NotifierTest.php
  46. 21 3
      tests/Service/OnChange/OnChangeContextTest.php
  47. 7 0
      tests/Service/OnChange/OnChangeDefaultTest.php
  48. 12 3
      tests/Service/OnChange/Organization/OnOrganizationChangeTest.php
  49. 30 3
      tests/Service/OnChange/Organization/OnParametersChangeTest.php
  50. 26 3
      tests/Service/OnChange/Organization/OnSubdomainChangeTest.php
  51. 26 2
      tests/Service/Organization/OrganizationProfileCreatorTest.php
  52. 180 9
      tests/Service/Organization/UtilsTest.php
  53. 28 3
      tests/Service/Rest/ApiRequestServiceTest.php
  54. 24 4
      tests/Service/Rest/Operation/BaseRestOperationTest.php
  55. 13 0
      tests/Service/Rest/Operation/CreateOperationTest.php
  56. 33 3
      tests/Service/Rest/Operation/DeleteOperationTest.php
  57. 18 3
      tests/Service/Rest/Operation/UpdateOperationTest.php
  58. 245 40
      tests/Service/Security/ModuleTest.php
  59. 2 1
      tests/Service/Security/SwitchUserTest.php
  60. 8 3
      tests/Service/ServiceIterator/CurrentAccessExtensionIteratorTest.php
  61. 9 0
      tests/Service/ServiceIterator/EncoderIteratorTest.php
  62. 9 2
      tests/Service/ServiceIterator/ExporterIteratorTest.php
  63. 10 4
      tests/Service/ServiceIterator/OptionalsRolesIteratorTest.php
  64. 2 0
      tests/Service/Storage/ApiLegacyStorageTest.php
  65. 78 3
      tests/Service/Storage/LocalStorageTest.php
  66. 52 2
      tests/Service/Typo3/Typo3ServiceTest.php
  67. 3 0
      tests/Service/Utils/ArrayUtilsTest.php
  68. 32 2
      tests/Service/Utils/DatesUtilsTest.php
  69. 80 0
      tests/Service/Utils/EntityUtilsTest.php
  70. 0 8
      tests/Service/Utils/GpsCoordinateUtilsTest.php
  71. 2 2
      tests/Service/Utils/ParserTest.php
  72. 109 0
      tests/Service/Utils/ReflectionTest.php
  73. 0 2
      tests/Service/Utils/SiretTest.php
  74. 21 5
      tests/Service/Utils/UrlBuilderTest.php
  75. 3 0
      tests/Service/Utils/UuidTest.php

+ 0 - 1
config/services.yaml

@@ -9,7 +9,6 @@ services:
         autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
         bind:
             $opentalentConfig: '%kernel.project_dir%%env(OPENTALENT_CONFIG)%'
-            $apiLegacyBaseUrl: '%env(API_LEG_BASE_URL)%'
             $internalFilesUploadUri: '%env(INTERNAL_FILES_DOWNLOAD_URI)%'
             $bindfileBufferFile: '%env(BIND_FILE_BUFFER_FILE)%'
             $contextAwareDataPersister: '@api_platform.doctrine.orm.data_persister'

+ 1 - 1
src/Doctrine/Access/AccessExtensionInterface.php

@@ -8,4 +8,4 @@ use Doctrine\ORM\QueryBuilder;
 interface AccessExtensionInterface{
     public function support(string $name);
     public function addWhere(QueryBuilder $queryBuilder);
-}
+}

+ 1 - 1
src/Doctrine/Access/Extensions/DateTimeConstraintExtension.php

@@ -26,4 +26,4 @@ class DateTimeConstraintExtension implements AccessExtensionInterface {
         $rootAlias = $queryBuilder->getRootAliases()[0];
         $queryBuilder->innerJoin(sprintf('%s.organizationFunction', $rootAlias), 'organization_function');
     }
-}
+}

+ 1 - 1
src/Entity/Core/File.php

@@ -161,7 +161,7 @@ class File
     private ?string $status = null;
 
     /**
-     * Statut du fichier (en cours de génération, prêt, supprimé, etc.)
+     * Serveur sur lequel le fichier est physiquement stocké
      * @var string | null
      */
     #[ORM\Column(length: 5)]

+ 1 - 0
src/Enum/Education/AdvancedEducationNotationTypeEnum.php

@@ -8,6 +8,7 @@ use MyCLabs\Enum\Enum;
 /**
  * Type possible sur lesquels les grilles péda doivent se baser
  * @method static BY_TEACHER()
+ * @method static BY_EDUCATION()
  */
 class AdvancedEducationNotationTypeEnum extends Enum
 {

+ 3 - 2
src/Service/Cotisation/Utils.php

@@ -8,6 +8,7 @@ use App\Enum\Cotisation\AlertStateEnum;
 use App\Repository\Cotisation\CotisationApiResourcesRepository;
 use App\Repository\Network\NetworkOrganizationRepository;
 use App\Service\Organization\Utils as OrganizationUtils;
+use App\Service\Utils\DatesUtils;
 use App\Tests\Service\Cotisation\UtilsTest;
 use App\Service\Network\Utils as NetworkUtils;
 
@@ -118,8 +119,8 @@ class Utils
      * @throws \Exception
      * @see UtilsTest::testGetCurrentCotisationYear()
      */
-    public function getCurrentCotisationYear(\DateTime $date = null): int {
-        $date = $date ?? new \DateTime('now');
+    public function getCurrentCotisationYear(): int {
+        $date = DatesUtils::new('now');
         $year = (int)$date->format('Y');
 
         $base_date = new \DateTime($year . '-09-01');

+ 6 - 6
src/Service/Export/BaseExporter.php

@@ -67,6 +67,12 @@ abstract class BaseExporter implements ExporterInterface
      */
     public function export(ExportRequest $exportRequest): File
     {
+        $requesterId = $exportRequest->getRequesterId();
+        $requester = $this->accessRepository->find($requesterId);
+        if ($requester === null) {
+            throw new \RuntimeException('Unable to determine the user; abort.');
+        }
+
         // Génère le modèle à partir de l'exportRequest
         $model = $this->buildModel($exportRequest);
 
@@ -76,12 +82,6 @@ abstract class BaseExporter implements ExporterInterface
         // Encode le html au format voulu
         $content = $this->encode($html, $exportRequest->getFormat());
 
-        $requesterId = $exportRequest->getRequesterId();
-        $requester = $this->accessRepository->find($requesterId);
-        if ($requester === null) {
-            throw new \RuntimeException('Unable to determine the user; abort.');
-        }
-
         // Met à jour ou créé l'enregistrement du fichier en base
         if ($exportRequest->getFileId() !== null) {
             $file = $this->fileRepository->find($exportRequest->getFileId());

+ 42 - 14
src/Service/Export/Model/LicenceCmf.php

@@ -100,9 +100,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param int $id
      */
-    public function setId(int $id): void
+    public function setId(int $id): self
     {
         $this->id = $id;
+
+        return $this;
     }
 
     /**
@@ -116,9 +118,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param int $year
      */
-    public function setYear(int $year): void
+    public function setYear(int $year): self
     {
         $this->year = $year;
+
+        return $this;
     }
 
     /**
@@ -132,9 +136,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param bool $isOrganizationLicence
      */
-    public function setIsOrganizationLicence(bool $isOrganizationLicence): void
+    public function setIsOrganizationLicence(bool $isOrganizationLicence): self
     {
         $this->isOrganizationLicence = $isOrganizationLicence;
+
+        return $this;
     }
 
     /**
@@ -148,9 +154,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param string $organizationName
      */
-    public function setOrganizationName(string $organizationName): void
+    public function setOrganizationName(string $organizationName): self
     {
         $this->organizationName = $organizationName;
+
+        return $this;
     }
 
     /**
@@ -164,9 +172,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param string|null $organizationIdentifier
      */
-    public function setOrganizationIdentifier(?string $organizationIdentifier): void
+    public function setOrganizationIdentifier(?string $organizationIdentifier): self
     {
         $this->organizationIdentifier = $organizationIdentifier;
+
+        return $this;
     }
 
     /**
@@ -180,9 +190,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param string $federationName
      */
-    public function setFederationName(string $federationName): void
+    public function setFederationName(string $federationName): self
     {
         $this->federationName = $federationName;
+
+        return $this;
     }
 
     /**
@@ -196,9 +208,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param string $color
      */
-    public function setColor(string $color): void
+    public function setColor(string $color): self
     {
         $this->color = $color;
+
+        return $this;
     }
 
     /**
@@ -212,9 +226,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param string $logoUri
      */
-    public function setLogoUri(string $logoUri): void
+    public function setLogoUri(string $logoUri): self
     {
         $this->logoUri = $logoUri;
+
+        return $this;
     }
 
     /**
@@ -228,9 +244,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param string|null $qrCodeUri
      */
-    public function setQrCodeUri(?string $qrCodeUri): void
+    public function setQrCodeUri(?string $qrCodeUri): self
     {
         $this->qrCodeUri = $qrCodeUri;
+
+        return $this;
     }
 
     /**
@@ -244,9 +262,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param int|null $personId
      */
-    public function setPersonId(?int $personId): void
+    public function setPersonId(?int $personId): self
     {
         $this->personId = $personId;
+
+        return $this;
     }
 
     /**
@@ -260,9 +280,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param string $personGender
      */
-    public function setPersonGender(string $personGender): void
+    public function setPersonGender(string $personGender): self
     {
         $this->personGender = $personGender;
+
+        return $this;
     }
 
     /**
@@ -276,9 +298,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param string $personFirstName
      */
-    public function setPersonFirstName(string $personFirstName): void
+    public function setPersonFirstName(string $personFirstName): self
     {
         $this->personFirstName = $personFirstName;
+
+        return $this;
     }
 
     /**
@@ -292,9 +316,11 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param string $personLastName
      */
-    public function setPersonLastName(string $personLastName): void
+    public function setPersonLastName(string $personLastName): self
     {
         $this->personLastName = $personLastName;
+
+        return $this;
     }
 
     /**
@@ -308,8 +334,10 @@ class LicenceCmf implements ExportModelInterface
     /**
      * @param string|null $personAvatarUri
      */
-    public function setPersonAvatarUri(?string $personAvatarUri): void
+    public function setPersonAvatarUri(?string $personAvatarUri): self
     {
         $this->personAvatarUri = $personAvatarUri;
+
+        return $this;
     }
 }

+ 1 - 1
src/Service/Export/Model/LicenceCmfCollection.php

@@ -10,7 +10,7 @@ class LicenceCmfCollection implements ExportModelInterface
      *
      * @var array
      */
-    private array $licences;
+    private array $licences = [];
 
     /**
      * @return array

+ 4 - 1
src/Service/Mobyt/MobytUserStatusCreator.php

@@ -24,12 +24,15 @@ class MobytUserStatusCreator
         $userStatus->setOrganizationId($organizationId);
 
         $organization = $this->organizationRepository->find($organizationId);
+        if ($organization === null) {
+            throw new \RuntimeException('Organization not found for user');
+        }
         $parameters = $organization->getParameters();
         $mobytLogin = $parameters->getUsernameSMS();
-        $mobytPassword = $parameters->getPasswordSMS();
         if (!$mobytLogin) {
             return $userStatus;
         }
+        $mobytPassword = $parameters->getPasswordSMS();
 
         $userStatusData = $this->mobytService->getUserStatus($mobytLogin, $mobytPassword);
         $userStatus->setActive(true);

+ 6 - 3
src/Service/Organization/Utils.php

@@ -6,6 +6,7 @@ namespace App\Service\Organization;
 use App\Entity\Organization\Organization;
 use App\Enum\Organization\OrganizationIdsEnum;
 use App\Enum\Organization\SettingsProductEnum;
+use App\Service\Utils\DatesUtils;
 use App\Service\Utils\UrlBuilder;
 use App\Test\Service\Organization\UtilsTest;
 use Doctrine\Common\Collections\Criteria;
@@ -77,15 +78,16 @@ class Utils
     }
 
     /**
-     * Retourne dans quelles année d'activité on se situe aujourd'hui
+     * Retourne l'année d'activité dans laquelle on se situe aujourd'hui
+     *
      * @param Organization $organization
      * @return int
      * @throws \Exception
      * @see UtilsTest::testGetOrganizationCurrentActivityYear()
      */
     public function getOrganizationCurrentActivityYear(Organization $organization): int{
-        $today = new \DateTime();
-        $year = intval($today->format('Y'));
+        $today = DatesUtils::new('now');
+        $year = (int)$today->format('Y');
 
         $musicalDate = $organization->getParameters()->getMusicalDate();
         if(empty($musicalDate)) $musicalDate = new \DateTime($year.'-08-31');
@@ -128,6 +130,7 @@ class Utils
 
     /**
      * Permet de retrouver quelle sera la date d'activité correspondant à une date précise de l'année
+     *
      * @param Organization $organization
      * @param \DateTimeInterface $date
      * @return int

+ 23 - 12
src/Service/Security/Module.php

@@ -17,14 +17,11 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  */
 class Module
 {
-    private array $moduleConfig;
-    private array $moduleByConditionsConfig;
+    private array | null $moduleConfig = null;
+    private array | null $moduleByConditionsConfig = null;
 
     public function __construct(private Reflection $reflection, private Parser $parser, private string $opentalentConfig)
-    {
-        $this->moduleConfig = $this->getModuleConfig();
-        $this->moduleByConditionsConfig = $this->getModuleByConditionsConfig();
-    }
+    {}
 
     /**
      * @todo activer le cache après que la fin de la migration.
@@ -80,7 +77,7 @@ class Module
      */
     public function getModulesByConditions(Organization $organization): array {
         $modulesByConditions = [];
-        $modules = $this->moduleByConditionsConfig['opentalent']['modulesbyconditions'];
+        $modules = $this->getModuleByConditionsConfig()['opentalent']['modulesbyconditions'];
         foreach ($modules as $moduleName => $module) {
             try{
                 $response = $this->reflection->dynamicInvokeServiceWithArgsAndMethod(
@@ -107,10 +104,10 @@ class Module
     public function getModulesByProductConfiguration(string $product): ?array {
         $product = str_replace('-', '_', $product);
 
-        if (!array_key_exists($product, $this->moduleConfig['opentalent']['products'])) {
+        if (!array_key_exists($product, $this->getModuleConfig()['opentalent']['products'])) {
             throw new AccessDeniedHttpException(sprintf('The product %s does not exist !', $product));
         }
-        $productConfig = $this->moduleConfig['opentalent']['products'][$product];
+        $productConfig = $this->getModuleConfig()['opentalent']['products'][$product];
         $modules = $productConfig['modules'] ?? [];
 
         if (array_key_exists('extend', $productConfig)) {
@@ -121,21 +118,35 @@ class Module
         return $modules;
     }
 
+    protected function getModuleConfig(): array {
+        if ($this->moduleConfig === null) {
+            $this->moduleConfig = $this->loadModuleConfig();
+        }
+        return $this->moduleConfig;
+    }
+
     /**
      * Parse et retourne le contenu du fichier products.yaml
      * @return array
      * @throws \Exception
      */
-    private function getModuleConfig(): array{
+    protected function loadModuleConfig(): array {
         return $this->parser->yamlParser($this->opentalentConfig, 'products.yaml');
     }
 
+    protected function getModuleByConditionsConfig(): array {
+        if ($this->moduleByConditionsConfig === null) {
+            $this->moduleByConditionsConfig = $this->loadModuleByConditionsConfig();
+        }
+        return $this->moduleByConditionsConfig;
+    }
+
     /**
      * Parse et retourne le contenu du fichier modulesbyconditions.yaml
      * @return array
      * @throws \Exception
      */
-    private function getModuleByConditionsConfig(): array{
+    protected function loadModuleByConditionsConfig(): array{
         return $this->parser->yamlParser($this->opentalentConfig, 'modulesbyconditions.yaml');
     }
 
@@ -145,7 +156,7 @@ class Module
      * @return int|null|string
      */
     public function getModuleByResourceName(string $resource): ?string {
-        $modules = $this->moduleConfig['opentalent']['modules'];
+        $modules = $this->getModuleConfig()['opentalent']['modules'];
         foreach ($modules as $module => $data) {
             if ($data['entities'] && in_array($resource, $data['entities'], true)) {
                 return $module;

+ 2 - 1
src/Service/ServiceIterator/CurrentAccessExtensionIterator.php

@@ -14,7 +14,8 @@ class CurrentAccessExtensionIterator {
         /** @var AccessExtensionInterface $extension */
         foreach ($this->extensions as $extension){
             if($extension->support($operationName)) {
-                return $extension->addWhere($queryBuilder);
+                $extension->addWhere($queryBuilder);
+                break;
             }
         }
     }

+ 35 - 3
src/Service/Utils/DatesUtils.php

@@ -3,22 +3,54 @@ declare(strict_types=1);
 
 namespace App\Service\Utils;
 
+use DateTimeZone;
+
 /**
  * Class Dates : méthodes d'aide pour la gestion de dates.
  * @package App\Service\Utils
  */
 class DatesUtils
 {
+    // For testing purpose, see setFakeDatetime and clearFakeDatetime method
+    protected static string | null $fakeDateTime = null;
+
+    /**
+     * @throws \Exception
+     */
+    public static function new(string $datetime = "now", ?DateTimeZone $timezone = null): \DateTime
+    {
+        if ($datetime === 'now') {
+            $datetime = static::$fakeDateTime ?? $datetime;
+        }
+        return new \DateTime($datetime, $timezone);
+    }
+
+    /**
+     * Set a fake datetime string that will be use in the 'new' method instead of whenever
+     * the passed datetime is 'now'
+     *
+     *
+     * @param string $datetime
+     */
+    public static function setFakeDatetime(string $datetime): void
+    {
+        self::$fakeDateTime = $datetime;
+    }
+
+    public static function clearFakeDatetime(): void
+    {
+        self::$fakeDateTime = null;
+    }
+
     /**
      * Vérifie si la date du jour est comprise dans l'interval passé en paramètres
      * @param \DateTime $dateStart
-     * @param \DateTime $dateEnd
+     * @param \DateTimeInterface|null $dateEnd
      * @return bool
-     * @throws \Exception
      * @see DatesUtilsTest::testIsIntervalIsValidNow()
      */
     public static function isIntervalIsValidNow(\DateTimeInterface $dateStart, \DateTimeInterface $dateEnd = null): bool {
         $now = new \DateTime('now');
         return $dateStart <= $now && (is_null($dateEnd) || $dateEnd >= $now);
     }
-}
+}

+ 6 - 4
src/Service/Utils/EntityUtils.php

@@ -13,7 +13,7 @@ use App\Entity\Access\Access;
  */
 class EntityUtils
 {
-    public function defaultValueSettersByAccess($entity, Access $access)
+    public function defaultValueSettersByAccess($entity, Access $access): void
     {
         $this->organizationDefaultValue($entity, $access);
         $this->billingSettingDefaultValueDefaultValue($entity, $access);
@@ -21,9 +21,10 @@ class EntityUtils
 
     /**
      * @param $entity
+     * @param Access $access
      * @throws \ReflectionException
      */
-    private function organizationDefaultValue($entity, Access $access)
+    protected function organizationDefaultValue($entity, Access $access): void
     {
         $reflection = new \ReflectionClass($entity::class);
         $organizationFaultValue = $reflection->getAttributes(OrganizationDefaultValue::class)[0] ?? null;
@@ -35,9 +36,10 @@ class EntityUtils
 
     /**
      * @param $entity
+     * @param Access $access
      * @throws \ReflectionException
      */
-    private function billingSettingDefaultValueDefaultValue($entity, Access $access)
+    protected function billingSettingDefaultValueDefaultValue($entity, Access $access): void
     {
         $reflection = new \ReflectionClass($entity::class);
         $billingSettingDefaultValueDefault = $reflection->getAttributes(BillingSettingDefaultValue::class)[0] ?? null;
@@ -46,4 +48,4 @@ class EntityUtils
             $entity->{sprintf('set%s', ucfirst($fieldName))}(...[$access->getOrganization()->getBillingSetting()]);
         }
     }
-}
+}

+ 2 - 2
src/Service/Utils/Parser.php

@@ -19,10 +19,10 @@ class Parser
      * @return array
      * @see ParserTest::testYamlParser()
      */
-    public function yamlParser(string $directory, string $yamlFile): array{
+    public function yamlParser(string $directory, string $yamlFile): array {
         $configDirectories = [$directory];
         $fileLocator = new FileLocator($configDirectories);
         $yamlConfig = $fileLocator->locate($yamlFile, null, false)[0];
         return Yaml::parseFile($yamlConfig);
     }
-}
+}

+ 21 - 15
src/Service/Utils/Reflection.php

@@ -3,6 +3,7 @@ declare(strict_types=1);
 
 namespace App\Service\Utils;
 
+use ReflectionException;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -12,10 +13,11 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
 class Reflection
 {
     public function __construct(private ContainerInterface $container)
-    { }
+    {}
 
     /**
-     * Appel une fonction avec ses paramètres depuis le nom d'un service
+     * Appelle une fonction avec ses paramètres depuis le nom d'un service
+     *
      * @param string $serviceName
      * @param string $method
      * @param array $parameters
@@ -24,29 +26,33 @@ class Reflection
     public function dynamicInvokeServiceWithArgsAndMethod(string $serviceName, string $method, array $parameters = []): mixed
     {
         $class = $this->container->get($serviceName);
-        //verif méthode exist
-        if(method_exists($class, $method))
-            return $class->{$method}(...$parameters);
-
-        throw new \LogicException("method_not_exist", 400);
+        if ($class === null) {
+            throw new \LogicException("no class found for service " . $serviceName, 400);
+        }
+        if (!method_exists($class, $method)) {
+            throw new \LogicException("method_not_exist", 400);
+        }
+        return $class->{$method}(...$parameters);
     }
 
     /**
-     * Appel une fonction avec ses paramètres  depuis le nom d'une classe
-     * @param string $serviceName
+     * Appelle une fonction avec ses paramètres depuis le nom d'une classe
+     *
+     * @param string $className
      * @param string $methodName
      * @param array $parameters
+     * @param array $constructorParameters
      * @return mixed
-     * @throws \ReflectionException
+     * @throws ReflectionException
      */
-    public function dynamicInvokeClassWithArgsAndMethod(string $className, string $methodName, array $parameters = []): mixed
+    public function dynamicInvokeClassWithArgsAndMethod(string $className, string $methodName, array $parameters = [], array $constructorParameters = []): mixed
     {
         $reflection = new \ReflectionClass($className);
         $method = $reflection->getMethod($methodName);
+
         if($method->isStatic()){
-            return $method->invoke(null, $parameters);
-        }else{
-            return $method->invoke($reflection, $parameters);
+            return $method->invoke(null, ...$parameters);
         }
+        return $method->invoke(new $className(...$constructorParameters), ...$parameters);
     }
-}
+}

+ 2 - 9
src/Service/Utils/Uuid.php

@@ -11,14 +11,7 @@ class Uuid
      */
     public static function uuid(int $length = 8): string
     {
-        try {
-            $uuid = \Ramsey\Uuid\Uuid::uuid4()->toString();
-        } catch (\Exception $e) {
-            throw new \RuntimeException('An error occurred while generating the UUID', 0, $e);
-        }
-        if ($length !== null) {
-            return substr($uuid, 0, $length);
-        }
-        return $uuid;
+        $uuid = \Ramsey\Uuid\Uuid::uuid4()->toString();
+        return substr($uuid, 0, $length);
     }
 }

+ 4 - 2
tests/Service/Access/AccessProfileCreatorTest.php

@@ -1,5 +1,5 @@
 <?php
-namespace App\Test\Service\Access;
+namespace App\Tests\Service\Access;
 
 use App\ApiResources\Profile\AccessProfile;
 use App\ApiResources\Profile\OrganizationProfile;
@@ -51,7 +51,8 @@ class AccessProfileCreatorTest extends TestCase
     /**
      * @see AccessProfileCreator::getAccessProfile()
      */
-    public function testGetAccessProfileFailed(){
+    public function testGetAccessProfileFailed(): void
+    {
 
         $this->accessProfileCreator = $this
             ->getMockBuilder(AccessProfileCreator::class)
@@ -205,6 +206,7 @@ class AccessProfileCreatorTest extends TestCase
 
     /**
      * Setup mocks for the tests on createCompleteAccessProfile
+     * @see AccessProfileCreator::createCompleteAccessProfile()
      */
     private function setupCreateCompleteAccessProfile(): void
     {

+ 5 - 3
tests/Service/Access/AdminAccessUtilsTest.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace App\Test\Service\Access;
+namespace App\Tests\Service\Access;
 
 use App\ApiResources\Access\AdminAccess;
 use App\Entity\Access\Access;
@@ -51,7 +51,8 @@ class AdminAccessUtilsTest extends TestCase
     /**
      * @see AdminAccessUtils::getAdminAccess()
      */
-    public function testGetAdminAccessWithoutContactPoint(){
+    public function testGetAdminAccessWithoutContactPoint(): void
+    {
         $adminAccessUtils = $this->getMockBuilder(AdminAccessUtils::class)
             ->setConstructorArgs([$this->accessUtils, $this->contactPointUtils])
             ->setMethodsExcept(['getAdminAccess'])
@@ -75,7 +76,8 @@ class AdminAccessUtilsTest extends TestCase
     /**
      * @see AdminAccessUtils::getAdminAccess()
      */
-    public function testGetAdminAccess(){
+    public function testGetAdminAccess(): void
+    {
         $adminAccessUtils = $this->getMockBuilder(AdminAccessUtils::class)
             ->setConstructorArgs([$this->accessUtils, $this->contactPointUtils])
             ->setMethodsExcept(['getAdminAccess'])

+ 18 - 2
tests/Service/Access/OptionalsRoles/CriteriaNotationOptionalRoleTest.php

@@ -1,4 +1,6 @@
-<?php /** @noinspection DuplicatedCode */
+<?php
+
+namespace App\Tests\Service\Access\OptionalsRoles;
 
 use App\Entity\Access\Access;
 use App\Entity\Organization\Organization;
@@ -6,6 +8,7 @@ use App\Entity\Organization\Parameters;
 use App\Enum\Access\FunctionEnum;
 use App\Repository\Access\AccessRepository;
 use App\Service\Access\OptionalsRoles\CriteriaNotationOptionalRole;
+use DateTime;
 use PHPUnit\Framework\TestCase;
 
 class CriteriaNotationOptionalRoleTest extends TestCase
@@ -16,6 +19,9 @@ class CriteriaNotationOptionalRoleTest extends TestCase
         $this->accessRepository = $this->getMockBuilder(AccessRepository::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see CriteriaNotationOptionalRole::support()
+     */
     public function testSupportIsSupported(): void
     {
         $criteriaNotationOptionalRole = $this
@@ -43,6 +49,9 @@ class CriteriaNotationOptionalRoleTest extends TestCase
         );
     }
 
+    /**
+     * @see CriteriaNotationOptionalRole::support()
+     */
     public function testSupportIsNotActiveTeacher(): void
     {
         $criteriaNotationOptionalRole = $this
@@ -70,6 +79,9 @@ class CriteriaNotationOptionalRoleTest extends TestCase
         );
     }
 
+    /**
+     * @see CriteriaNotationOptionalRole::support()
+     */
     public function testSupportIsActiveTeacherButAdminOnly(): void
     {
         $criteriaNotationOptionalRole = $this
@@ -97,7 +109,11 @@ class CriteriaNotationOptionalRoleTest extends TestCase
         );
     }
 
-    public function testGetRole() {
+    /**
+     * @see CriteriaNotationOptionalRole::getRole()
+     */
+    public function testGetRole(): void
+    {
         $criteriaNotationOptionalRole = $this
             ->getMockBuilder(CriteriaNotationOptionalRole::class)
             ->setConstructorArgs([$this->accessRepository])

+ 1 - 1
tests/Service/Access/UtilsTest.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace App\Test\Service\Access;
+namespace App\Tests\Service\Access;
 
 use App\Entity\Access\Access;
 use App\Entity\Organization\Organization;

+ 20 - 2
tests/Service/ApiLegacy/ApiLegacyRequestServiceTest.php

@@ -1,6 +1,6 @@
-<?php /** @noinspection DuplicatedCode */
+<?php
 
-namespace App\Test\Service\ApiLegacy;
+namespace App\Tests\Service\ApiLegacy;
 
 use App\Entity\Access\Access;
 use App\Service\ApiLegacy\ApiLegacyRequestService;
@@ -24,6 +24,9 @@ class ApiLegacyRequestServiceTest extends TestCase
         $this->security = $this->getMockBuilder(Security::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see ApiLegacyRequestService::request()
+     */
     public function testRequest(): void
     {
         $api1RequestService = $this
@@ -67,6 +70,9 @@ class ApiLegacyRequestServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see ApiLegacyRequestService::request()
+     */
     public function testRequestNoToken(): void
     {
         $api1RequestService = $this
@@ -83,6 +89,9 @@ class ApiLegacyRequestServiceTest extends TestCase
         $api1RequestService->request('GET', '/an/url');
     }
 
+    /**
+     * @see ApiLegacyRequestService::request()
+     */
     public function testRequestNullToken(): void
     {
         $api1RequestService = $this
@@ -101,6 +110,9 @@ class ApiLegacyRequestServiceTest extends TestCase
         $api1RequestService->request('GET', '/an/url');
     }
 
+    /**
+     * @see ApiLegacyRequestService::request()
+     */
     public function testRequestInvalidToken(): void
     {
         $api1RequestService = $this
@@ -120,6 +132,9 @@ class ApiLegacyRequestServiceTest extends TestCase
         $api1RequestService->request('GET', '/an/url');
     }
 
+    /**
+     * @see ApiLegacyRequestService::request()
+     */
     public function testRequestSwitchUser(): void
     {
         $api1RequestService = $this
@@ -166,6 +181,9 @@ class ApiLegacyRequestServiceTest extends TestCase
         $api1RequestService->request('GET', '/an/url');
     }
 
+    /**
+     * @see ApiLegacyRequestService::request()
+     */
     public function testRequestSwitchInvalidUser(): void
     {
         $api1RequestService = $this

+ 6 - 3
tests/Service/Constraint/AbstractTimeConstraintsUtilsTest.php

@@ -14,7 +14,8 @@ class AbstractTimeConstraintsUtilsTest extends TestCase
      * @throws \ReflectionException
      * @see DateTimeConstraint::hasCustomPeriods()
      */
-    public function testHasCustomPeriods(){
+    public function testHasCustomPeriods(): void
+    {
         $historical = ['dateStart' => '2020-09-01', 'dateEnd' => '2021-08-31'];
 
         $timeConstraintUtils = $this->getMockBuilder(AbstractTimeConstraintUtils::class)
@@ -29,7 +30,8 @@ class AbstractTimeConstraintsUtilsTest extends TestCase
      * @throws \ReflectionException
      * @see DateTimeConstraint::hasCustomPeriods()
      */
-    public function testHasNotCustomPeriods(){
+    public function testHasNotCustomPeriods(): void
+    {
         $historical = ['dateStart' => null, 'dateEnd' => null];
 
         $timeConstraintUtils = $this->getMockBuilder(AbstractTimeConstraintUtils::class)
@@ -43,7 +45,8 @@ class AbstractTimeConstraintsUtilsTest extends TestCase
     /**
      * @see DateTimeConstraint::addConstraint()
      */
-    public function testAddConstraint(){
+    public function testAddConstraint(): void
+    {
         $originalConstraint = [
             'start' => [],
             'end' => []

+ 31 - 1
tests/Service/Constraint/ActivityYearConstraintTest.php

@@ -1,4 +1,4 @@
-<?php /** @noinspection DuplicatedCode */
+<?php
 
 namespace App\Tests\Service\Constraint;
 
@@ -33,6 +33,9 @@ class ActivityYearConstraintTest extends TestCase
         $this->organizationUtils = $this->getMockBuilder(OrganizationUtils::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see ActivityYearConstraint::invoke()
+     */
     public function testInvokePresentNoCustomPeriods(): void
     {
         $activityYearConstraint = $this->getMockBuilder(TestableActivityYearConstraint::class)
@@ -71,6 +74,9 @@ class ActivityYearConstraintTest extends TestCase
         $activityYearConstraint->invoke(123);
     }
 
+    /**
+     * @see ActivityYearConstraint::invoke()
+     */
     public function testInvokePastNoCustomPeriods(): void
     {
         $activityYearConstraint = $this->getMockBuilder(TestableActivityYearConstraint::class)
@@ -109,6 +115,9 @@ class ActivityYearConstraintTest extends TestCase
         $activityYearConstraint->invoke(123);
     }
 
+    /**
+     * @see ActivityYearConstraint::invoke()
+     */
     public function testInvokeFutureNoCustomPeriods(): void
     {
         $activityYearConstraint = $this->getMockBuilder(TestableActivityYearConstraint::class)
@@ -147,6 +156,9 @@ class ActivityYearConstraintTest extends TestCase
         $activityYearConstraint->invoke(123);
     }
 
+    /**
+     * @see ActivityYearConstraint::invoke()
+     */
     public function testInvokeMultiplePeriodsNoCustom(): void
     {
         $activityYearConstraint = $this->getMockBuilder(TestableActivityYearConstraint::class)
@@ -189,6 +201,9 @@ class ActivityYearConstraintTest extends TestCase
         $activityYearConstraint->invoke(123);
     }
 
+    /**
+     * @see ActivityYearConstraint::invoke()
+     */
     public function testInvokeWithCustom(): void {
         $activityYearConstraint = $this->getMockBuilder(TestableActivityYearConstraint::class)
             ->setConstructorArgs([$this->entityManager, $this->organizationUtils])
@@ -234,6 +249,9 @@ class ActivityYearConstraintTest extends TestCase
         $activityYearConstraint->invoke(123);
     }
 
+    /**
+     * @see ActivityYearConstraint::getRangeYear()
+     */
     public function testGetRangeYear(): void {
         $activityYearConstraint = $this->getMockBuilder(TestableActivityYearConstraint::class)
             ->setConstructorArgs([$this->entityManager, $this->organizationUtils])
@@ -261,6 +279,9 @@ class ActivityYearConstraintTest extends TestCase
         );
     }
 
+    /**
+     * @see ActivityYearConstraint::presentConstraint()
+     */
     public function testPresentConstraint(): void {
         $activityYearConstraint = $this->getMockBuilder(TestableActivityYearConstraint::class)
             ->setConstructorArgs([$this->entityManager, $this->organizationUtils])
@@ -273,6 +294,9 @@ class ActivityYearConstraintTest extends TestCase
         );
     }
 
+    /**
+     * @see ActivityYearConstraint::pastConstraint()
+     */
     public function testPastConstraint(): void {
         $activityYearConstraint = $this->getMockBuilder(TestableActivityYearConstraint::class)
             ->setConstructorArgs([$this->entityManager, $this->organizationUtils])
@@ -285,6 +309,9 @@ class ActivityYearConstraintTest extends TestCase
         );
     }
 
+    /**
+     * @see ActivityYearConstraint::futureConstraint()
+     */
     public function testFutureConstraint(): void {
         $activityYearConstraint = $this->getMockBuilder(TestableActivityYearConstraint::class)
             ->setConstructorArgs([$this->entityManager, $this->organizationUtils])
@@ -297,6 +324,9 @@ class ActivityYearConstraintTest extends TestCase
         );
     }
 
+    /**
+     * @see ActivityYearConstraint::customConstraint()
+     */
     public function testCustomConstraint(): void {
         $activityYearConstraint = $this->getMockBuilder(TestableActivityYearConstraint::class)
             ->setConstructorArgs([$this->entityManager, $this->organizationUtils])

+ 18 - 0
tests/Service/Constraint/DateTimeConstraintTest.php

@@ -45,6 +45,9 @@ class DateTimeConstraintTest extends TestCase
        ];
    }
 
+   /**
+    * @see DateTimeConstraint::invoke()
+    */
    public function testInvokePresentNoCustomPeriods(): void {
        $dateTimeConstraint = $this->getMockBuilder(TestableDateTimeConstraint::class)
            ->setConstructorArgs([$this->entityManager, $this->organizationUtils])
@@ -83,6 +86,9 @@ class DateTimeConstraintTest extends TestCase
        $dateTimeConstraint->invoke(123);
    }
 
+    /**
+     * @see DateTimeConstraint::invoke()
+     */
     public function testInvokePastNoCustomPeriods(): void
     {
         $dateTimeConstraint = $this->getMockBuilder(TestableDateTimeConstraint::class)
@@ -123,6 +129,9 @@ class DateTimeConstraintTest extends TestCase
         $dateTimeConstraint->invoke(123);
     }
 
+    /**
+     * @see DateTimeConstraint::invoke()
+     */
     public function testInvokeFutureNoCustomPeriods(): void
     {
         $dateTimeConstraint = $this->getMockBuilder(TestableDateTimeConstraint::class)
@@ -163,6 +172,9 @@ class DateTimeConstraintTest extends TestCase
         $dateTimeConstraint->invoke(123);
     }
 
+    /**
+     * @see DateTimeConstraint::invoke()
+     */
     public function testInvokeMultiplePeriodsNoCustom(): void
     {
         $dateTimeConstraint = $this->getMockBuilder(TestableDateTimeConstraint::class)
@@ -205,6 +217,9 @@ class DateTimeConstraintTest extends TestCase
         $dateTimeConstraint->invoke(123);
     }
 
+    /**
+     * @see DateTimeConstraint::invoke()
+     */
     public function testInvokeWithCustom(): void {
         $dateTimeConstraint = $this->getMockBuilder(TestableDateTimeConstraint::class)
             ->setConstructorArgs([$this->entityManager, $this->organizationUtils])
@@ -251,6 +266,9 @@ class DateTimeConstraintTest extends TestCase
         $dateTimeConstraint->invoke(123);
     }
 
+    /**
+     * @see DateTimeConstraint::getCustomPeriods()
+     */
    public function testGetCustomPeriods(): void {
        $dateTimeConstraint = $this->getMockBuilder(TestableDateTimeConstraint::class)
            ->setConstructorArgs([$this->entityManager, $this->organizationUtils])

+ 19 - 3
tests/Service/Core/AddressPostalUtilsTest.php

@@ -1,15 +1,26 @@
 <?php
 
-namespace App\Test\Service\Access;
+namespace App\Tests\Service\Core;
 
 use App\Entity\Core\AddressPostal;
 use App\Service\Core\AddressPostalUtils;
 use PHPUnit\Framework\TestCase;
 
+class TestableAddressPostalUtils extends AddressPostalUtils {
+    // there's a bug when SUT has only one method,
+    // it can't be mocked, so we add one method here in the meantime
+    public function foo(): void {}
+}
+
 class AddressPostalUtilsTest extends TestCase
 {
+    /**
+     * @see AddressPostalUtils::getFullStreetAddress()
+     */
     public function testGetFullStreetAddress(): void {
-        $addressPostalUtils = new AddressPostalUtils(); // Cf. bug when SUT has only one method, can't be mocked
+        $addressPostalUtils = $this->getMockBuilder(TestableAddressPostalUtils::class)
+            ->setMethodsExcept(['getFullStreetAddress'])
+            ->getMock();
 
         $addressPostal = $this->getMockBuilder(AddressPostal::class)->getMock();
         $addressPostal->method('getStreetAddress')->willReturn('Abc');
@@ -27,8 +38,13 @@ class AddressPostalUtilsTest extends TestCase
         );
     }
 
+    /**
+     * @see AddressPostalUtils::getFullStreetAddress()
+     */
     public function testGetFullStreetAddressWithMissing(): void {
-        $addressPostalUtils = new AddressPostalUtils(); // Cf. bug when SUT has only one method, can't be mocked
+        $addressPostalUtils = $this->getMockBuilder(TestableAddressPostalUtils::class)
+            ->setMethodsExcept(['getFullStreetAddress'])
+            ->getMock();
 
         $addressPostal = $this->getMockBuilder(AddressPostal::class)->getMock();
         $addressPostal->method('getStreetAddress')->willReturn('Abc');

+ 1 - 1
tests/Service/Core/ContactPointUtilsTest.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace App\Test\Service\Access;
+namespace App\Tests\Service\Core;
 
 use App\Entity\Access\Access;
 use App\Entity\Core\ContactPoint;

+ 5 - 1
tests/Service/Cotisation/CotisationCreatorTest.php

@@ -20,7 +20,11 @@ class CotisationCreatorTest extends TestCase
         $this->cotisationUtils = $this->getMockBuilder(Utils::class)->disableOriginalConstructor()->getMock();
     }
 
-    public function testGetCotisation() {
+    /**
+     * @see CotisationCreator::getCotisation()
+     */
+    public function testGetCotisation(): void
+    {
         $cotisationCreator = $this->getMockBuilder(CotisationCreator::class)
             ->setConstructorArgs([$this->organizationRepository, $this->cotisationUtils])
             ->setMethodsExcept(['getCotisation'])

+ 9 - 22
tests/Service/Cotisation/UtilsTest.php

@@ -8,9 +8,10 @@ use App\Repository\Cotisation\CotisationApiResourcesRepository;
 use App\Repository\Network\NetworkOrganizationRepository;
 use App\Service\Cotisation\Utils as CotisationUtils;
 use App\Service\Organization\Utils as OrganizationUtils;
+use App\Service\Utils\DatesUtils;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
-use \App\Service\Network\Utils as NetworkUtils;
+use App\Service\Network\Utils as NetworkUtils;
 
 class UtilsTest extends TestCase
 {
@@ -325,25 +326,6 @@ class UtilsTest extends TestCase
         $this->assertEquals(AlertStateEnum::ADVERTISINGINSURANCE()->getValue(), $cotisationUtils->getAlertState($organization, $year) );
     }
 
-    /**
-     * @see Utils::getCurrentCotisationYear()
-     */
-    public function testGetCurrentCotisationYearDefault(): void
-    {
-        $cotisationUtils = $this->getMockBuilder(CotisationUtils::class)
-            ->setConstructorArgs([$this->networkUtils, $this->organizationUtils, $this->networkOrganizationRepository, $this->cotisationApiResourcesRepository])
-            ->setMethodsExcept(['getCurrentCotisationYear'])
-            ->getMock();
-
-        $today = new \DateTime('now');
-        $expectedYear = (int)$today->format('Y');
-        if($today->format('m') > 9) {
-            $expectedYear++;
-        }
-
-        $this->assertEquals($expectedYear, $cotisationUtils->getCurrentCotisationYear());
-    }
-
     /**
      * @see Utils::getCurrentCotisationYear()
      */
@@ -354,13 +336,18 @@ class UtilsTest extends TestCase
             ->setMethodsExcept(['getCurrentCotisationYear'])
             ->getMock();
 
+        DatesUtils::setFakeDatetime('2022-03-01');
+
         $this->assertEquals(
             2022,
-            $cotisationUtils->getCurrentCotisationYear(new \DateTime('2022-03-01'))
+            $cotisationUtils->getCurrentCotisationYear()
         );
+
+        DatesUtils::setFakeDatetime('2022-10-01');
+
         $this->assertEquals(
             2023,
-            $cotisationUtils->getCurrentCotisationYear(new \DateTime('2022-10-01'))
+            $cotisationUtils->getCurrentCotisationYear()
         );
     }
 }

+ 23 - 3
tests/Service/Dolibarr/DolibarrAccountCreatorTest.php

@@ -1,4 +1,4 @@
-<?php /** @noinspection PhpUnhandledExceptionInspection */
+<?php
 
 namespace App\Tests\Service\Dolibarr;
 
@@ -19,6 +19,9 @@ class DolibarrAccountCreatorTest extends TestCase
             ->getMock();
     }
 
+    /**
+     * @see DolibarrAccountCreator::getDolibarrAccount()
+     */
     public function testGetDolibarrAccount(): void
     {
         $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
@@ -79,6 +82,9 @@ class DolibarrAccountCreatorTest extends TestCase
         $this->assertEquals($dolibarrAccount, $result);
     }
 
+    /**
+     * @see DolibarrAccountCreator::createDolibarrAccount()
+     */
     public function testCreateDolibarrAccount(): void
     {
         $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
@@ -97,6 +103,9 @@ class DolibarrAccountCreatorTest extends TestCase
         $this->assertEquals(null, $dolibarrAccount->getProduct());
     }
 
+    /**
+     * @see DolibarrAccountCreator::createDolibarrAccount()
+     */
     public function testCreateDolibarrAccountWithProduct(): void
     {
         $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
@@ -112,6 +121,9 @@ class DolibarrAccountCreatorTest extends TestCase
         $this->assertEquals('PRODUCT_ARTIST', $dolibarrAccount->getProduct());
     }
 
+    /**
+     * @see DolibarrAccountCreator::createDolibarrContract()
+     */
     public function testCreateDolibarrContract(): void
     {
         $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
@@ -136,7 +148,11 @@ class DolibarrAccountCreatorTest extends TestCase
         $this->assertEquals(123, $dolibarrContract->getSocId());
     }
 
-    public function testCreateDolibarrContractLine() {
+    /**
+     * @see DolibarrAccountCreator::createDolibarrContractLine()
+     */
+    public function testCreateDolibarrContractLine(): void
+    {
         $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
             ->setConstructorArgs([$this->dolibarrApiService])
             ->setMethodsExcept(['createDolibarrContractLine'])
@@ -161,7 +177,11 @@ class DolibarrAccountCreatorTest extends TestCase
         $this->assertEquals('2020-12-31T00:00:00+00:00', $dolibarrContractLine->getDateEnd()->format('c'));
     }
 
-    public function testCreateDolibarrBill() {
+    /**
+     * @see DolibarrAccountCreator::createDolibarrBill()
+     */
+    public function testCreateDolibarrBill(): void
+    {
 
         $dolibarrAccountCreator = $this->getMockBuilder(DolibarrAccountCreator::class)
             ->setConstructorArgs([$this->dolibarrApiService])

+ 4 - 2
tests/Service/Dolibarr/DolibarrApiServiceTest.php

@@ -186,7 +186,8 @@ class DolibarrApiServiceTest extends TestCase
     /**
      * @see DolibarrApiService::getContacts()
      */
-    public function testGetContacts() {
+    public function testGetContacts(): void
+    {
         $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
             ->setConstructorArgs([$this->client])
             ->setMethodsExcept(['getContacts'])
@@ -249,7 +250,8 @@ class DolibarrApiServiceTest extends TestCase
     /**
      * @see DolibarrApiService::getActiveOpentalentContacts()
      */
-    public function testGetActiveOpentalentContacts() {
+    public function testGetActiveOpentalentContacts(): void
+    {
         $dolibarrApiService = $this->getMockBuilder(DolibarrApiService::class)
             ->setConstructorArgs([$this->client])
             ->setMethodsExcept(['getActiveOpentalentContacts'])

+ 80 - 2
tests/Service/Dolibarr/DolibarrSyncServiceTest.php

@@ -102,7 +102,7 @@ class DolibarrSyncServiceTest extends TestCase
     /**
      * Full test of the scan method
      *
-     * @throws \Exception
+     * @see DolibarrSyncService::scan()
      */
     public function testScan(): void
     {
@@ -571,7 +571,7 @@ class DolibarrSyncServiceTest extends TestCase
      * 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
+     * @see DolibarrSyncService::execute()
      */
     public function testExecute(): void
     {
@@ -618,6 +618,9 @@ class DolibarrSyncServiceTest extends TestCase
         $dolibarrSyncService->execute([$operation1, $operation2, $operation3], $progressionCallback);
     }
 
+    /**
+     * @see DolibarrSyncService::run()
+     */
     public function testRun() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -635,6 +638,9 @@ class DolibarrSyncServiceTest extends TestCase
         $this->assertEquals($operations, $result);
     }
 
+    /**
+     * @see DolibarrSyncService::getDolibarrSocietiesIndex()
+     */
     public function testGetDolibarrSocietiesIndex(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -674,6 +680,9 @@ class DolibarrSyncServiceTest extends TestCase
 
     }
 
+    /**
+     * @see DolibarrSyncService::getActiveMembersIndex()
+     */
     public function testGetActiveMembersIndex(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -702,6 +711,9 @@ class DolibarrSyncServiceTest extends TestCase
         ], $index);
     }
 
+    /**
+     * @see DolibarrSyncService::findDolibarrContactFor()
+     */
     public function testFindDolibarrContactForById(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -723,6 +735,9 @@ class DolibarrSyncServiceTest extends TestCase
         $this->assertEquals(2, $contact['id']);
     }
 
+    /**
+     * @see DolibarrSyncService::findDolibarrContactFor()
+     */
     public function testFindDolibarrContactForByName(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -747,6 +762,9 @@ class DolibarrSyncServiceTest extends TestCase
         $this->assertEquals(1, $result['id']);
     }
 
+    /**
+     * @see DolibarrSyncService::findDolibarrContactFor()
+     */
     public function testFindDolibarrContactForByNameWithConflict(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -772,6 +790,9 @@ class DolibarrSyncServiceTest extends TestCase
 
     }
 
+    /**
+     * @see DolibarrSyncService::findDolibarrContactFor()
+     */
     public function testFindDolibarrContactNotFound(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -796,6 +817,9 @@ class DolibarrSyncServiceTest extends TestCase
         $this->assertEquals(null, $result);
     }
 
+    /**
+     * @see DolibarrSyncService::sanitizeDolibarrData()
+     */
     public function testSanitizeDolibarrData(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -812,6 +836,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::sanitizeDolibarrData()
+     */
     public function testSanitizeDolibarrDataWithNull(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -824,6 +851,9 @@ class DolibarrSyncServiceTest extends TestCase
         $this->assertEquals(null, $result);
     }
 
+    /**
+     * @see DolibarrSyncService::getOrganizationPostalAddress()
+     */
     public function testGetOrganizationPostalAddress(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -856,6 +886,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::getOrganizationPostalAddress()
+     */
     public function testGetOrganizationPostalAddressNoResult(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -875,6 +908,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::getOrganizationPhone()
+     */
     public function testGetOrganizationPhoneWithExistingPhone(): void
     {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
@@ -910,6 +946,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::getOrganizationPhone()
+     */
     public function testGetOrganizationPhoneWithMobilePhone() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -946,6 +985,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::getOrganizationPhone()
+     */
     public function testGetOrganizationPhoneWithNoPhone() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -967,6 +1009,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::getOrganizationEmail()
+     */
     public function testGetOrganizationEmailWithExistingEmail() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -998,6 +1043,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::getOrganizationEmail()
+     */
     public function testGetOrganizationEmailWithNoEmail() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -1017,6 +1065,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::getOrganizationNetworkId()
+     */
     public function testGetOrganizationNetworkId() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -1037,6 +1088,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::getOrganizationNetworkId()
+     */
     public function testGetOrganizationNetworkIdWithMultipleResult() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -1067,6 +1121,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::getOrganizationNetworkId()
+     */
     public function testGetOrganizationNetworkIdWithNoResult() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -1088,6 +1145,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::countWithMission()
+     */
     public function testCountWithMission() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -1126,6 +1186,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::getPersonContact()
+     */
     public function testGetPersonContact() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -1157,6 +1220,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::formatContactPosition()
+     */
     public function testFormatContactPosition() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -1234,6 +1300,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::formatPhoneNumber()
+     */
     public function testFormatPhoneNumber() {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -1249,6 +1318,9 @@ class DolibarrSyncServiceTest extends TestCase
         );
     }
 
+    /**
+     * @see DolibarrSyncService::validateResponse()
+     */
     public function testValidateResponse(): void {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -1269,6 +1341,9 @@ class DolibarrSyncServiceTest extends TestCase
         $dolibarrSyncService->validateResponse($response, $operation);
     }
 
+    /**
+     * @see DolibarrSyncService::validateResponse()
+     */
     public function testValidateResponseInvalid(): void {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,
@@ -1296,6 +1371,9 @@ class DolibarrSyncServiceTest extends TestCase
         $dolibarrSyncService->validateResponse($response, $operation);
     }
 
+    /**
+     * @see DolibarrSyncService::validateResponse()
+     */
     public function testValidateResponseRequestError(): void {
         $dolibarrSyncService = $this->getMockBuilder(TestableDolibarrSyncService::class)
             ->setConstructorArgs([$this->organizationRepository, $this->accessRepository, $this->functionTypeRepository,

+ 7 - 1
tests/Service/Education/EducationNotationUtilsTest.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace App\Test\Service\Access;
+namespace App\Tests\Service\Education;
 
 use App\Entity\Access\Access;
 use App\Entity\Education\CriteriaNotation;
@@ -90,6 +90,9 @@ class EducationNotationUtilsTest extends TestCase
         $this->assertNull($educationNotationUtils->getNotationOriginal($educationNotation));
     }
 
+    /**
+     * @see EducationNotationUtils::calculNotationByAMaxNote()
+     */
     public function testCalculNotationByAMaxNote(): void
     {
         $educationNotationUtils = $this->getMockBuilder(TestableEducationNotationUtils::class)
@@ -134,6 +137,7 @@ class EducationNotationUtilsTest extends TestCase
 
     /**
      * If $educationNotation has no note, it should return null
+     * @see EducationNotationUtils::calculNotationByAMaxNote()
      */
     public function testCalculNotationByAMaxNoteWithoutNote(): void
     {
@@ -155,6 +159,7 @@ class EducationNotationUtilsTest extends TestCase
 
     /**
      * If $educationNotation has no criteriaNotation, it should return null
+     * @see EducationNotationUtils::calculNotationByAMaxNote()
      */
     public function testCalculNotationByAMaxNoteWithoutCriteria(): void
     {
@@ -177,6 +182,7 @@ class EducationNotationUtilsTest extends TestCase
 
     /**
      * If $criteriaNotation has WITHOUT_NOTATION type, this should return null
+     * @see EducationNotationUtils::calculNotationByAMaxNote()
      */
     public function testCalculNotationByAMaxNoteWithoutNotation(): void
     {

+ 5 - 1
tests/Service/Elasticsearch/EducationNotationUpdaterTest.php

@@ -9,7 +9,8 @@ use PHPUnit\Framework\TestCase;
 class TestableEducationNotationUpdater extends EducationNotationUpdater {
     // To avoid the bug when mocking classes with only one method;
     // can be removed when the tested class will have at least 2 methods
-    public function foo() {}
+    public function foo(): void
+    {}
 }
 
 class EducationNotationUpdaterTest extends TestCase
@@ -20,6 +21,9 @@ class EducationNotationUpdaterTest extends TestCase
         $this->objectPersister = $this->getMockBuilder(ObjectPersister::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see EducationNotationUpdater::update()
+     */
     public function testUpdate(): void {
         $educationNotationUpdater = $this->getMockBuilder(TestableEducationNotationUpdater::class)
             ->setConstructorArgs([$this->objectPersister])

+ 181 - 9
tests/Service/Export/BaseExporterTest.php

@@ -10,6 +10,7 @@ use App\Enum\Core\FileTypeEnum;
 use App\Repository\Access\AccessRepository;
 use App\Repository\Core\FileRepository;
 use App\Service\Export\BaseExporter;
+use App\Service\Export\Encoder\EncoderInterface;
 use App\Service\Export\Model\ExportModelInterface;
 use App\Service\ServiceIterator\EncoderIterator;
 use App\Service\Storage\LocalStorage;
@@ -18,6 +19,7 @@ use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Psr\Log\LoggerInterface;
 use Twig\Environment;
+use Twig\Error\SyntaxError;
 
 class TestableBaseExporter extends BaseExporter {
     public function buildModel(ExportRequest $exportRequest): ExportModelInterface { return parent::buildModel($exportRequest); }
@@ -68,6 +70,9 @@ class BaseExporterTest extends TestCase
         return $exporter;
     }
 
+    /**
+     * @see BaseExporter::support()
+     */
     public function testSupport(): void {
         $exporter = $this->getBaseExporterMockFor('support');
 
@@ -76,6 +81,9 @@ class BaseExporterTest extends TestCase
         $this->assertFalse($exporter->support($exportRequest));
     }
 
+    /**
+     * @see BaseExporter::export()
+     */
     public function testExport(): void {
         $exporter = $this->getBaseExporterMockFor('export');
 
@@ -103,6 +111,9 @@ class BaseExporterTest extends TestCase
         $exporter->export($exportRequest);
     }
 
+    /**
+     * @see BaseExporter::export()
+     */
     public function testExportNewFile(): void {
         $exporter = $this->getBaseExporterMockFor('export');
 
@@ -132,6 +143,9 @@ class BaseExporterTest extends TestCase
         $exporter->export($exportRequest);
     }
 
+    /**
+     * @see BaseExporter::export()
+     */
     public function testExportUndefinedRequester(): void {
         $exporter = $this->getBaseExporterMockFor('export');
 
@@ -139,24 +153,19 @@ class BaseExporterTest extends TestCase
         $exportRequest->method('getRequesterId')->willReturn(123);
         $exportRequest->method('getFileId')->willReturn(456);
 
-        $model = $this->getMockBuilder(ExportModelInterface::class)->disableOriginalConstructor()->getMock();
-        $exporter->expects(self::once())->method('buildModel')->with($exportRequest)->willReturn($model);
-
-        $html = "<div>foo</div>";
-        $exporter->expects(self::once())->method('render')->with($model)->willReturn($html);
-
-        $data = 'azerty';
-        $exporter->expects(self::once())->method('encode')->with($html)->willReturn($data);
-
         $this->accessRepository->method('find')->with(123, null, null)->willReturn(null);
 
         $this->storage->expects(self::never())->method('writeFile');
 
         $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage('Unable to determine the user; abort.');
 
         $exporter->export($exportRequest);
     }
 
+    /**
+     * @see BaseExporter::export()
+     */
     public function testExportUndefinedOrganization(): void {
         $exporter = $this->getBaseExporterMockFor('export');
 
@@ -180,7 +189,170 @@ class BaseExporterTest extends TestCase
         $this->storage->expects(self::never())->method('writeFile');
 
         $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage('Unable to determine the organization of the curent user; abort.');
 
         $exporter->export($exportRequest);
     }
+
+    /**
+     * @see BaseExporter::prepareFile()
+     */
+    public function testPrepareFile(): void {
+        $exporter = $this->getBaseExporterMockFor('prepareFile');
+
+        $exportRequest = $this->getMockBuilder(ExportRequest::class)->disableOriginalConstructor()->getMock();
+        $exportRequest->method('getRequesterId')->willReturn(123);
+        $exportRequest->method('getFormat')->willReturn('pdf');
+
+        $access = $this->getMockBuilder(Access::class)->disableOriginalConstructor()->getMock();
+        $this->accessRepository->method('find')->with(123, null, null)->willReturn($access);
+
+        $exporter->method('getFileBasename')->with($exportRequest)->willReturn('Foo');
+        $exporter->method('getFileType')->willReturn(FileTypeEnum::UNKNOWN());
+
+        $file = $this->getMockBuilder(File::class)->disableOriginalConstructor()->getMock();
+
+        $this->storage->expects(self::once())
+            ->method('prepareFile')
+            ->with(
+                $access, 'Foo.pdf', FileTypeEnum::UNKNOWN(), $access, true, 'NOBODY', 'application/pdf', false
+            )->willReturn($file);
+
+        $result = $exporter->prepareFile($exportRequest, false);
+
+        $this->assertSame($file, $result);
+    }
+
+    /**
+     * @see BaseExporter::prepareFile()
+     */
+    public function testPrepareFileUndefinedRequester(): void {
+        $exporter = $this->getBaseExporterMockFor('prepareFile');
+
+        $exportRequest = $this->getMockBuilder(ExportRequest::class)->disableOriginalConstructor()->getMock();
+        $exportRequest->method('getRequesterId')->willReturn(123);
+        $this->accessRepository->method('find')->with(123, null, null)->willReturn(null);
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage('Unable to determine the current user; abort.');
+
+        $exporter->prepareFile($exportRequest, false);
+    }
+
+    /**
+     * @see BaseExporter::buildModel()
+     */
+    public function testBuildModel(): void {
+        $exporter = $this->getBaseExporterMockFor('buildModel');
+
+        $exportRequest = $this->getMockBuilder(ExportRequest::class)->disableOriginalConstructor()->getMock();
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage('not implemented error');
+
+        $exporter->buildModel($exportRequest);
+    }
+
+    /**
+     * @see BaseExporter::getBasename()
+     */
+    public function testGetBaseName(): void {
+        $exporter = new TestableBaseExporter();
+
+        $this->assertEquals('testable_base', $exporter->getBasename());
+    }
+
+    /**
+     * @see BaseExporter::getTemplatePath()
+     */
+    public function testGetTemplatePath(): void {
+        $exporter = $this->getBaseExporterMockFor('getTemplatePath');
+        $exporter->method('getBaseName')->willReturn('base_exporter');
+
+        $this->assertEquals('@templates/export/base_exporter.html.twig', $exporter->getTemplatePath());
+    }
+
+    /**
+     * @see BaseExporter::render()
+     */
+    public function testRender(): void {
+        $exporter = $this->getBaseExporterMockFor('render');
+
+        $exporter->method('getTemplatePath')->willReturn('/path/to/template.html.twig');
+
+        $model = $this->getMockBuilder(ExportModelInterface::class)->disableOriginalConstructor()->getMock();
+
+        $content = "azerty";
+
+        $this->twig->expects(self::once())
+            ->method('render')
+            ->with('/path/to/template.html.twig', ['model' => $model])
+            ->willReturn($content);
+
+        $result = $exporter->render($model);
+
+        $this->assertEquals($content, $result);
+    }
+
+    /**
+     * @see BaseExporter::render()
+     */
+    public function testRenderError(): void {
+        $exporter = $this->getBaseExporterMockFor('render');
+
+        $exporter->method('getTemplatePath')->willReturn('/path/to/template.html.twig');
+
+        $model = $this->getMockBuilder(ExportModelInterface::class)->disableOriginalConstructor()->getMock();
+
+        $this->twig
+            ->expects(self::once())
+            ->method('render')
+            ->with('/path/to/template.html.twig', ['model' => $model])
+            ->willThrowException(new SyntaxError('error'));
+
+        $this->expectException(\RuntimeException::class);
+
+        $exporter->render($model);
+    }
+
+    /**
+     * @see BaseExporter::encode()
+     */
+    public function testEncode(): void {
+        $exporter = $this->getBaseExporterMockFor('encode');
+
+        $encoder = $this->getMockBuilder(EncoderInterface::class)->getMock();
+
+        $this->encoderIterator->expects(self::once())->method('getEncoderFor')->with('pdf')->willReturn($encoder);
+
+        $html = '<span>foo</span>';
+        $content = 'foo';
+        $encoder->expects(self::once())->method('encode')->with($html)->willReturn($content);
+
+        $result = $exporter->encode($html, 'pdf');
+
+        $this->assertEquals($content, $result);
+    }
+
+    /**
+     * @see BaseExporter::getFileBasename()
+     */
+    public function testGetFileBasename(): void {
+        $exporter = $this->getBaseExporterMockFor('getFileBasename');
+
+        $exportRequest = $this->getMockBuilder(ExportRequest::class)->disableOriginalConstructor()->getMock();
+
+        $exporter->method('getBasename')->willReturn('base');
+
+        $this->assertEquals('base', $exporter->getFileBasename($exportRequest));
+    }
+
+    /**
+     * @see BaseExporter::getFileType()
+     */
+    public function testGetFileType(): void {
+        $exporter = $this->getBaseExporterMockFor('getFileType');
+
+        $this->assertEquals(FileTypeEnum::UNKNOWN(), $exporter->getFileType());
+    }
 }

+ 11 - 0
tests/Service/Export/Encoder/PdfEncoderTest.php

@@ -1,5 +1,7 @@
 <?php
 
+namespace App\Tests\Service\Export\Encoder;
+
 use App\Service\Export\Encoder\PdfEncoder;
 use Knp\Snappy\Pdf;
 use PHPUnit\Framework\MockObject\MockObject;
@@ -14,6 +16,9 @@ class PdfEncoderTest extends TestCase
         $this->knpSnappy = $this->getMockBuilder(Pdf::class)->getMock();
     }
 
+    /**
+     * @see PdfEncoder::support()
+     */
     public function testSupport(): void
     {
         $encoder = $this->getMockBuilder(PdfEncoder::class)
@@ -25,6 +30,9 @@ class PdfEncoderTest extends TestCase
         $this->assertFalse($encoder->support('txt'));
     }
 
+    /**
+     * @see PdfEncoder::getDefaultOptions()
+     */
     public function testGetDefaultOptions(): void
     {
         $encoder = $this->getMockBuilder(PdfEncoder::class)
@@ -35,6 +43,9 @@ class PdfEncoderTest extends TestCase
         $this->assertIsArray($encoder->getDefaultOptions());
     }
 
+    /**
+     * @see PdfEncoder::encode()
+     */
     public function testEncode(): void
     {
         $encoder = $this->getMockBuilder(PdfEncoder::class)

+ 18 - 1
tests/Service/Export/LicenceCmfExporterTest.php

@@ -1,5 +1,7 @@
 <?php /** @noinspection PhpUnhandledExceptionInspection */
 
+namespace App\Tests\Service\Export;
+
 use App\ApiResources\Export\ExportRequest;
 use App\ApiResources\Export\LicenceCmf\LicenceCmfOrganizationER;
 use App\Entity\Access\Access;
@@ -13,7 +15,6 @@ use App\Repository\Organization\OrganizationRepository;
 use App\Service\Export\Encoder\PdfEncoder;
 use App\Service\Export\LicenceCmfExporter;
 use App\Service\Export\Model\LicenceCmf;
-use App\Service\Export\Model\LicenceCmfCollection;
 use App\Service\ServiceIterator\EncoderIterator;
 use App\Service\Storage\LocalStorage;
 use App\Tests\TestToolsTrait;
@@ -86,6 +87,9 @@ class LicenceCmfExporterTest extends TestCase
         return $exporter;
     }
 
+    /**
+     * @see LicenceCmfExporter::support()
+     */
     public function testSupport(): void
     {
         $exporter = $this->makeExporterMock('support');
@@ -130,6 +134,9 @@ class LicenceCmfExporterTest extends TestCase
         $this->organizationRepo->expects(self::once())->method('find')->with(12097)->willReturn($this->cmf);
     }
 
+    /**
+     * @see LicenceCmfExporter::buildModel()
+     */
     public function testBuildModel(): void
     {
         $exporter = $this->makeExporterMock('buildModel');
@@ -150,6 +157,7 @@ class LicenceCmfExporterTest extends TestCase
 
     /**
      * If the organization cannot be determined from the exportRequest author, an error should be thrown
+     * @see LicenceCmfExporter::buildModel()
      */
     public function testBuildModelWithoutOrganization(): void
     {
@@ -163,6 +171,9 @@ class LicenceCmfExporterTest extends TestCase
         $this->invokeMethod($exporter, 'buildModel', [$this->exportRequest]);
     }
 
+    /**
+     * @see LicenceCmfExporter::getFileBasename()
+     */
     public function testGetFileBasename(): void
     {
         $exporter = $this->makeExporterMock('getFileBasename');
@@ -181,6 +192,9 @@ class LicenceCmfExporterTest extends TestCase
         );
     }
 
+    /**
+     * @see LicenceCmfExporter::getFileType()
+     */
     public function testGetFileType(): void
     {
         $exporter = $this->makeExporterMock('getFileType');
@@ -193,6 +207,7 @@ class LicenceCmfExporterTest extends TestCase
 
     /**
      * Given year is before the start year, first color is used
+     * @see LicenceCmfExporter::getLicenceColor()
      */
     public function testGetLicenceColorPastYear(): void
     {
@@ -208,6 +223,7 @@ class LicenceCmfExporterTest extends TestCase
 
     /**
      * Given year is within the 5 first years
+     * @see LicenceCmfExporter::getLicenceColor()
      */
     public function testGetLicenceColorFirstYears(): void
     {
@@ -223,6 +239,7 @@ class LicenceCmfExporterTest extends TestCase
 
     /**
      * Given year is beyond the 5 first years
+     * @see LicenceCmfExporter::getLicenceColor()
      */
     public function testGetLicenceColorNExtYears(): void
     {

+ 32 - 0
tests/Service/Export/Model/LicenceCmfCollectionTest.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Tests\Service\Export\Model;
+
+use App\Service\Export\Model\LicenceCmf;
+use App\Service\Export\Model\LicenceCmfCollection;
+use PHPUnit\Framework\TestCase;
+
+class LicenceCmfCollectionTest extends TestCase
+{
+    /**
+     * @see LicenceCmf::getLicences()
+     * @see LicenceCmf::addLicence()
+     * @see LicenceCmf::setLicences()
+     */
+    public function testSetters(): void {
+        $collection = new LicenceCmfCollection();
+
+        $model = $this->getMockBuilder(LicenceCmf::class)->getMock();
+
+        $this->assertEquals([], $collection->getLicences());
+
+        $collection->addLicence($model);
+
+        $this->assertEquals([$model], $collection->getLicences());
+
+        $collection->setLicences([]);
+
+        $this->assertEquals([], $collection->getLicences());
+    }
+
+}

+ 75 - 0
tests/Service/Export/Model/LicenceCmfTest.php

@@ -0,0 +1,75 @@
+<?php
+
+namespace App\Tests\Service\Export\Model;
+
+use App\Service\Export\Model\LicenceCmf;
+use PHPUnit\Framework\TestCase;
+
+class LicenceCmfTest extends TestCase
+{
+    /**
+     * @see LicenceCmf::setId()
+     * @see LicenceCmf::setYear()
+     * @see LicenceCmf::setIsOrganizationLicence()
+     * @see LicenceCmf::setOrganizationName()
+     * @see LicenceCmf::setOrganizationIdentifier()
+     * @see LicenceCmf::setFederationName()
+     * @see LicenceCmf::setColor()
+     * @see LicenceCmf::setLogoUri()
+     * @see LicenceCmf::setQrCodeUri()
+     * @see LicenceCmf::setPersonId()
+     * @see LicenceCmf::setPersonGender()
+     * @see LicenceCmf::setPersonFirstName()
+     * @see LicenceCmf::setPersonLastName()
+     * @see LicenceCmf::setPersonAvatarUri()
+     *
+     * @see LicenceCmf::getId()
+     * @see LicenceCmf::getYear()
+     * @see LicenceCmf::getIsOrganizationLicence()
+     * @see LicenceCmf::getOrganizationName()
+     * @see LicenceCmf::getOrganizationIdentifier()
+     * @see LicenceCmf::getFederationName()
+     * @see LicenceCmf::getColor()
+     * @see LicenceCmf::getLogoUri()
+     * @see LicenceCmf::getQrCodeUri()
+     * @see LicenceCmf::getPersonId()
+     * @see LicenceCmf::getPersonGender()
+     * @see LicenceCmf::getPersonFirstName()
+     * @see LicenceCmf::getPersonLastName()
+     * @see LicenceCmf::getPersonAvatarUri()
+     */
+    public function testSetters(): void {
+        $model = new LicenceCmf();
+        $model
+            ->setId(1)
+            ->setYear(2020)
+            ->setIsOrganizationLicence(true)
+            ->setOrganizationName('foo')
+            ->setOrganizationIdentifier('123')
+            ->setFederationName('bar')
+            ->setColor('#55555')
+            ->setLogoUri('/file/1')
+            ->setQrCodeUri('file/2')
+            ->setPersonId(2)
+            ->setPersonGender('MR')
+            ->setPersonFirstName('Don Diego')
+            ->setPersonLastName('De la Vega')
+            ->setPersonAvatarUri('/file/3');
+
+        $this->assertEquals(1, $model->getId());
+        $this->assertEquals(2020, $model->getYear());
+        $this->assertEquals(true, $model->isOrganizationLicence());
+        $this->assertEquals('foo', $model->getOrganizationName());
+        $this->assertEquals('123', $model->getOrganizationIdentifier());
+        $this->assertEquals('bar', $model->getFederationName());
+        $this->assertEquals('#55555', $model->getColor());
+        $this->assertEquals('/file/1', $model->getLogoUri());
+        $this->assertEquals('file/2', $model->getQrCodeUri());
+        $this->assertEquals(2, $model->getPersonId());
+        $this->assertEquals('MR', $model->getPersonGender());
+        $this->assertEquals('Don Diego', $model->getPersonFirstName());
+        $this->assertEquals('De la Vega', $model->getPersonLastName());
+        $this->assertEquals('/file/3', $model->getPersonAvatarUri());
+    }
+
+}

+ 17 - 2
tests/Service/MailHubTest.php

@@ -1,7 +1,10 @@
 <?php /** @noinspection PhpUnhandledExceptionInspection */
 
+namespace App\Tests\Service;
+
 use App\Entity\Access\Access;
 use App\Entity\Core\ContactPoint;
+use App\Entity\Organization\Organization;
 use App\Entity\Person\Person;
 use App\Service\Access\Utils as AccessUtils;
 use App\Service\Core\ContactPointUtils;
@@ -24,6 +27,9 @@ class MailHubTest extends TestCase
         $this->accessUtils = $this->getMockBuilder(AccessUtils::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see MailHub::sendAutomaticEmailTo()
+     */
     public function testSendAutomaticEmailTo(): void
     {
         $mailerHub = $this->getMockBuilder(MailHub::class)
@@ -51,6 +57,9 @@ class MailHubTest extends TestCase
         $mailerHub->sendAutomaticEmailTo($access, 'subject', 'a_template', []);
     }
 
+    /**
+     * @see MailHub::sendAutomaticEmailTo()
+     */
     public function testSendAutomaticEmailToButNoAddress(): void
     {
         $mailerHub = $this->getMockBuilder(MailHub::class)
@@ -67,6 +76,9 @@ class MailHubTest extends TestCase
         $mailerHub->sendAutomaticEmailTo($access, 'subject', 'a_template', []);
     }
 
+    /**
+     * @see MailHub::sendAutomaticEmailToAdmin()
+     */
     public function testSendAutomaticEmailToAdmin(): void
     {
         $mailerHub = $this->getMockBuilder(MailHub::class)
@@ -74,7 +86,7 @@ class MailHubTest extends TestCase
             ->setMethodsExcept(['sendAutomaticEmailToAdmin'])
             ->getMock();
 
-        $organization = $this->getMockBuilder(\App\Entity\Organization\Organization::class)->disableOriginalConstructor()->getMock();
+        $organization = $this->getMockBuilder(Organization::class)->disableOriginalConstructor()->getMock();
         $admin = $this->getMockBuilder(Access::class)->disableOriginalConstructor()->getMock();
 
         $this->accessUtils->expects(self::once())->method('findAdminFor')->with($organization)->willReturn($admin);
@@ -87,6 +99,9 @@ class MailHubTest extends TestCase
         $mailerHub->sendAutomaticEmailToAdmin($organization, 'subject', 'template', []);
     }
 
+    /**
+     * @see MailHub::sendAutomaticEmailToAdmin()
+     */
     public function testSendAutomaticEmailToAdminButNoAdmin(): void
     {
         $mailerHub = $this->getMockBuilder(MailHub::class)
@@ -94,7 +109,7 @@ class MailHubTest extends TestCase
             ->setMethodsExcept(['sendAutomaticEmailToAdmin'])
             ->getMock();
 
-        $organization = $this->getMockBuilder(\App\Entity\Organization\Organization::class)->disableOriginalConstructor()->getMock();
+        $organization = $this->getMockBuilder(Organization::class)->disableOriginalConstructor()->getMock();
 
         $this->accessUtils
             ->expects(self::once())

+ 24 - 6
tests/Service/MercureHubTest.php

@@ -3,8 +3,8 @@
 namespace App\Tests\Service;
 
 use ApiPlatform\Core\Api\IriConverterInterface;
+use App\Entity\Organization\Organization;
 use App\Service\MercureHub;
-use Doctrine\Entity;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Mercure\HubInterface;
 use Symfony\Component\Mercure\Update;
@@ -29,6 +29,9 @@ class MercureHubTest extends TestCase
         $this->iriConverter = $this->getMockBuilder(IriConverterInterface::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see MercureHub::createUpdate()
+     */
     public function testCreateUpdate(): void {
         $mercureHub = $this
             ->getMockBuilder(TestableMercureHub::class)
@@ -43,6 +46,9 @@ class MercureHubTest extends TestCase
         $this->assertEquals(true, $update->isPrivate());
     }
 
+    /**
+     * @see MercureHub::publish()
+     */
     public function testPublish(): void
     {
         $mercureHub = $this
@@ -51,7 +57,7 @@ class MercureHubTest extends TestCase
             ->setMethodsExcept(['publish'])
             ->getMock();
 
-        $entity = $this->getMockBuilder(Entity::class)->disableOriginalConstructor()->getMock();
+        $entity = $this->getMockBuilder(Organization::class)->disableOriginalConstructor()->getMock();
 
         $this->iriConverter->expects(self::once())->method('getIriFromItem')->with($entity)->willReturn('/api/entity/1');
         $this->serializer->expects(self::once())->method('serialize')->with($entity, 'jsonld', [])->willReturn("{'foo': 1}");
@@ -83,6 +89,9 @@ class MercureHubTest extends TestCase
         $mercureHub->publish(1, $entity);
     }
 
+    /**
+     * @see MercureHub::publish()
+     */
     public function testPublishInvalidOperation(): void
     {
         $mercureHub = $this
@@ -91,12 +100,15 @@ class MercureHubTest extends TestCase
             ->setMethodsExcept(['publish'])
             ->getMock();
 
-        $entity = $this->getMockBuilder(Entity::class)->disableOriginalConstructor()->getMock();
+        $entity = $this->getMockBuilder(Organization::class)->disableOriginalConstructor()->getMock();
 
         $this->expectException(\InvalidArgumentException::class);
         $mercureHub->publish(1, $entity, 'foo');
     }
 
+    /**
+     * @see MercureHub::publishUpdate()
+     */
     public function testPublishUpdate(): void {
         $mercureHub = $this
             ->getMockBuilder(TestableMercureHub::class)
@@ -104,12 +116,15 @@ class MercureHubTest extends TestCase
             ->setMethodsExcept(['publishUpdate'])
             ->getMock();
 
-        $entity = $this->getMockBuilder(Entity::class)->disableOriginalConstructor()->getMock();
+        $entity = $this->getMockBuilder(Organization::class)->disableOriginalConstructor()->getMock();
 
         $mercureHub->expects(self::once())->method('publish')->with(1, $entity, 'update');
         $mercureHub->publishUpdate(1, $entity);
     }
 
+    /**
+     * @see MercureHub::publishCreate()
+     */
     public function testPublishCreate(): void {
         $mercureHub = $this
             ->getMockBuilder(TestableMercureHub::class)
@@ -117,13 +132,16 @@ class MercureHubTest extends TestCase
             ->setMethodsExcept(['publishCreate'])
             ->getMock();
 
-        $entity = $this->getMockBuilder(Entity::class)->disableOriginalConstructor()->getMock();
+        $entity = $this->getMockBuilder(Organization::class)->disableOriginalConstructor()->getMock();
 
         $mercureHub->expects(self::once())->method('publish')->with(1, $entity, 'create');
 
         $mercureHub->publishCreate(1, $entity);
     }
 
+    /**
+     * @see MercureHub::publishDelete()
+     */
     public function testPublishDelete(): void {
         $mercureHub = $this
             ->getMockBuilder(TestableMercureHub::class)
@@ -131,7 +149,7 @@ class MercureHubTest extends TestCase
             ->setMethodsExcept(['publishDelete'])
             ->getMock();
 
-        $entity = $this->getMockBuilder(Entity::class)->disableOriginalConstructor()->getMock();
+        $entity = $this->getMockBuilder(Organization::class)->disableOriginalConstructor()->getMock();
 
         $mercureHub->expects(self::once())->method('publish')->with(1, $entity, 'delete');
 

+ 3 - 2
tests/Service/Mobyt/MobytServiceTest.php

@@ -6,7 +6,6 @@ use App\Service\Mobyt\MobytService;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Symfony\Contracts\HttpClient\HttpClientInterface;
-use Symfony\Contracts\HttpClient\ResponseInterface;
 
 class TestableMobytService extends MobytService {
     public function connect(string $login, string $password): void { parent::connect($login, $password); }
@@ -26,7 +25,9 @@ class MobytServiceTest extends TestCase
     }
 
     /**
-     * @see MobytService::getUserStatus()
+     * @see MobytService::connect()
+     * @see MobytService::getUserId()
+     * @see MobytService::getSessionKey()
      */
     public function testConnect(): void {
 

+ 80 - 5
tests/Service/Mobyt/MobytUserStatusCreatorTest.php

@@ -24,6 +24,9 @@ class MobytUserStatusCreatorTest extends TestCase
             ->getMock();
     }
 
+    /**
+     * @see MobytUserStatusCreator::getUserStatus()
+     */
     public function testGetUserStatus(): void
     {
         $mobytUserStatusCreator = $this->getMockBuilder(MobytUserStatusCreator::class)
@@ -66,14 +69,86 @@ class MobytUserStatusCreatorTest extends TestCase
         $mobytUserStatus = $mobytUserStatusCreator->getUserStatus($organizationId);
 
         $this->assertEquals(
-            $mobytUserStatus->getMoney(),
-            33.0
+            33.0,
+            $mobytUserStatus->getMoney()
         );
 
-        // check that top-level amoung is taken in account, and only it
+        // check that top-level amount is taken in account, and only it
         $this->assertEquals(
-            $mobytUserStatus->getAmount(),
-            300
+            300,
+            $mobytUserStatus->getAmount()
+        );
+    }
+
+    /**
+     * @see MobytUserStatusCreator::getUserStatus()
+     */
+    public function testGetUserStatusInvalidOrganization(): void
+    {
+        $mobytUserStatusCreator = $this->getMockBuilder(MobytUserStatusCreator::class)
+            ->setConstructorArgs([$this->mobytService, $this->organizationRepository])
+            ->setMethodsExcept(['getUserStatus'])
+            ->getMock();
+
+        $organizationId = 123;
+
+        $this->organizationRepository
+            ->expects($this->once())
+            ->method('find')
+            ->with($organizationId)
+            ->willReturn(null);
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage('Organization not found for user');
+
+        $mobytUserStatusCreator->getUserStatus($organizationId);
+    }
+
+    /**
+     * @see MobytUserStatusCreator::getUserStatus()
+     */
+    public function testGetUserStatusMissingAuthInformation(): void
+    {
+        $mobytUserStatusCreator = $this->getMockBuilder(MobytUserStatusCreator::class)
+            ->setConstructorArgs([$this->mobytService, $this->organizationRepository])
+            ->setMethodsExcept(['getUserStatus'])
+            ->getMock();
+
+        $organizationId = 123;
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $this->organizationRepository
+            ->expects($this->once())
+            ->method('find')
+            ->with($organizationId)
+            ->willReturn($organization);
+
+        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
+        $organization->expects($this->once())->method('getParameters')->willReturn($parameters);
+
+        $parameters->expects($this->once())->method('getUsernameSMS')->willReturn(null);
+
+        $this->mobytService
+            ->expects($this->never())
+            ->method('getUserStatus');
+
+        $mobytUserStatus = $mobytUserStatusCreator->getUserStatus($organizationId);
+
+        $this->assertEquals(
+            123,
+            $mobytUserStatus->getOrganizationId()
+        );
+
+        $this->assertEquals(
+            0,
+            $mobytUserStatus->getMoney()
+
+        );
+
+        // check that top-level amount is taken in account, and only it
+        $this->assertEquals(
+            0,
+            $mobytUserStatus->getAmount()
         );
     }
 }

+ 18 - 0
tests/Service/NotifierTest.php

@@ -28,6 +28,9 @@ class NotifierTest extends TestCase
         $this->mercureHub = $this->getMockBuilder(MercureHub::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see Notifier::createNotification()
+     */
     public function testCreateNotification(): void {
         $notifier = $this
             ->getMockBuilder(TestableNotifier::class)
@@ -58,6 +61,9 @@ class NotifierTest extends TestCase
         $this->assertInstanceOf(\DateTime::class, $notification->getUpdateDate());
     }
 
+    /**
+     * @see Notifier::notify()
+     */
     public function testNotify(): void {
         $notifier = $this
             ->getMockBuilder(TestableNotifier::class)
@@ -84,6 +90,9 @@ class NotifierTest extends TestCase
         $this->assertEquals($notification, $returned);
     }
 
+    /**
+     * @see Notifier::notifyExport()
+     */
     public function testNotifyExport(): void {
         $notifier = $this
             ->getMockBuilder(TestableNotifier::class)
@@ -104,6 +113,9 @@ class NotifierTest extends TestCase
         $notifier->notifyExport($access, $file);
     }
 
+    /**
+     * @see Notifier::notifyMessage()
+     */
     public function testNotifyMessage(): void {
         $notifier = $this
             ->getMockBuilder(TestableNotifier::class)
@@ -121,6 +133,9 @@ class NotifierTest extends TestCase
         $notifier->notifyMessage($access, ['a message']);
     }
 
+    /**
+     * @see Notifier::notifySystem()
+     */
     public function testNotifySystem(): void {
         $notifier = $this
             ->getMockBuilder(TestableNotifier::class)
@@ -138,6 +153,9 @@ class NotifierTest extends TestCase
         $notifier->notifySystem($access, ['a message']);
     }
 
+    /**
+     * @see Notifier::notifyError()
+     */
     public function testNotifyError(): void {
         $notifier = $this
             ->getMockBuilder(TestableNotifier::class)

+ 21 - 3
tests/Service/OnChange/OnChangeContextTest.php

@@ -1,11 +1,19 @@
 <?php
 
+namespace App\Tests\Service\OnChange;
+
 use App\Service\OnChange\OnChangeContext;
 use PHPUnit\Framework\TestCase;
 
 class OnChangeContextTest extends TestCase
 {
-    public function testIsPostRequest() {
+    /**
+     * @see OnChangeContext::__construct()
+     * @see OnChangeContext::isPostRequest()
+     * @see OnChangeContext::isPutRequest()
+     */
+    public function testIsPostRequest(): void
+    {
         $context = new OnChangeContext(
             ['collection_operation_name' => 'post']
         );
@@ -14,7 +22,13 @@ class OnChangeContextTest extends TestCase
         $this->assertFalse($context->isPutRequest());
     }
 
-    public function testIsPutRequest() {
+    /**
+     * @see OnChangeContext::__construct()
+     * @see OnChangeContext::isPostRequest()
+     * @see OnChangeContext::isPutRequest()
+     */
+    public function testIsPutRequest(): void
+    {
         $context = new OnChangeContext(
             ['item_operation_name' => 'put']
         );
@@ -23,7 +37,11 @@ class OnChangeContextTest extends TestCase
         $this->assertFalse($context->isPostRequest());
     }
 
-    public function testPreviousData() {
+    /**
+     * @see OnChangeContext::previousData()
+     */
+    public function testPreviousData(): void
+    {
         $context = new OnChangeContext(
             ['previous_data' => 1]
         );

+ 7 - 0
tests/Service/OnChange/OnChangeDefaultTest.php

@@ -1,5 +1,7 @@
 <?php
 
+namespace App\Tests\Service\OnChange;
+
 use App\Service\OnChange\OnChangeContext;
 use App\Service\OnChange\OnChangeDefault;
 use PHPUnit\Framework\TestCase;
@@ -8,6 +10,11 @@ class OnChangeDefaultTest extends TestCase
 {
     /**
      * Default OnChange service does nothing; it shouldn't change anything to the data nor raise exceptions
+     *
+     * @see OnChangeDefault::validate()
+     * @see OnChangeDefault::preProcess()
+     * @see OnChangeDefault::beforeChange()
+     * @see OnChangeDefault::onChange()
      */
     public function testDoesNothing(): void
     {

+ 12 - 3
tests/Service/OnChange/Organization/OnOrganizationChangeTest.php

@@ -1,5 +1,6 @@
 <?php
-namespace App\Test\Service\OnChange\Organization;
+
+namespace App\Tests\Service\OnChange\Organization;
 
 use App\Entity\Billing\BillingSetting;
 use App\Entity\Organization\Organization;
@@ -11,6 +12,9 @@ use PHPUnit\Framework\TestCase;
 
 class OnOrganizationChangeTest extends TestCase
 {
+    /**
+     * @see OnOrganizationChange::beforeChange()
+     */
     public function testBeforeChangeNoChange(): void
     {
         $onOrganizationChange =  $this
@@ -36,6 +40,9 @@ class OnOrganizationChangeTest extends TestCase
         $onOrganizationChange->beforeChange($organization, $context);
     }
 
+    /**
+     * @see OnOrganizationChange::beforeChange()
+     */
     public function testBeforeChangeLegalStatusChanged(): void
     {
         $onOrganizationChange =  $this
@@ -64,7 +71,8 @@ class OnOrganizationChangeTest extends TestCase
     /**
      * @see OnOrganizationChange::onLegalStatusChange()
      */
-    public function testOnLegalStatusChangeToAsso(){
+    public function testOnLegalStatusChangeToAsso(): void
+    {
 
         $onOrganizationChange =  $this
             ->getMockBuilder(OnOrganizationChange::class)
@@ -90,7 +98,8 @@ class OnOrganizationChangeTest extends TestCase
     /**
      * @see OnOrganizationChange::onLegalStatusChange()
      */
-    public function testOnLegalStatusChangeToSociety(){
+    public function testOnLegalStatusChangeToSociety(): void
+    {
 
         $onOrganizationChange =  $this
             ->getMockBuilder(OnOrganizationChange::class)

+ 30 - 3
tests/Service/OnChange/Organization/OnParametersChangeTest.php

@@ -1,8 +1,8 @@
-<?php /** @noinspection DuplicatedCode */
+<?php
 
 /** @noinspection PhpUnhandledExceptionInspection */
 
-namespace App\Test\Service\OnChange\Organization;
+namespace App\Tests\Service\OnChange\Organization;
 
 use App\Entity\Access\Access;
 use App\Entity\Booking\Course;
@@ -41,6 +41,9 @@ class OnParametersChangeTest extends TestCase
         $this->messageBus = $this->getMockBuilder(MessageBusInterface::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see OnParametersChange::validate()
+     */
     public function testValidateValid(): void
     {
         $onParametersChange = $this->getMockBuilder(OnParametersChange::class)
@@ -70,6 +73,9 @@ class OnParametersChangeTest extends TestCase
         }
     }
 
+    /**
+     * @see OnParametersChange::validate()
+     */
     public function testValidateInvalid(): void
     {
         $onParametersChange = $this->getMockBuilder(OnParametersChange::class)
@@ -92,6 +98,9 @@ class OnParametersChangeTest extends TestCase
         $onParametersChange->validate($parameters, $context);
     }
 
+    /**
+     * @see OnParametersChange::beforeChange()
+     */
     public function testBeforeChange(): void
     {
         $onParametersChange = $this
@@ -121,6 +130,9 @@ class OnParametersChangeTest extends TestCase
         $onParametersChange->beforeChange($parameters, $context);
     }
 
+    /**
+     * @see OnParametersChange::beforeChange()
+     */
     public function testBeforeChangeNoChange(): void
     {
         $onParametersChange = $this
@@ -151,6 +163,9 @@ class OnParametersChangeTest extends TestCase
         $onParametersChange->beforeChange($parameters, $context);
     }
 
+    /**
+     * @see OnParametersChange::onChange()
+     */
     public function testOnChangeNoChange(): void
     {
         $onParametersChange = $this
@@ -179,6 +194,9 @@ class OnParametersChangeTest extends TestCase
         $onParametersChange->onChange($parameters, $context);
     }
 
+    /**
+     * @see OnParametersChange::onChange()
+     */
     public function testOnChangeAverageChanged(): void
     {
         $onParametersChange = $this
@@ -211,6 +229,9 @@ class OnParametersChangeTest extends TestCase
         $onParametersChange->onChange($parameters, $context);
     }
 
+    /**
+     * @see OnParametersChange::onChange()
+     */
     public function testOnChangeCustomDomainChanged(): void
     {
         $onParametersChange = $this
@@ -247,6 +268,9 @@ class OnParametersChangeTest extends TestCase
         $onParametersChange->onChange($parameters, $context);
     }
 
+    /**
+     * @see OnParametersChange::onChange()
+     */
     public function testOnChangeWebsiteDisabled(): void
     {
         $onParametersChange = $this
@@ -283,6 +307,9 @@ class OnParametersChangeTest extends TestCase
         $onParametersChange->onChange($parameters, $context);
     }
 
+    /**
+     * @see OnParametersChange::onChange()
+     */
     public function testOnChangeWebsiteEnabled(): void
     {
         $onParametersChange = $this
@@ -294,7 +321,7 @@ class OnParametersChangeTest extends TestCase
         $this->messageBus
             ->expects(self::exactly(2))
             ->method('dispatch')
-            ->willReturnCallback(function ($message, $stamps = []) {
+            ->willReturnCallback(function ($message) {
                 if ($message instanceof Typo3UndeleteCommand) {
                     return new Envelope(new Typo3UndeleteCommand(1));
                 }

+ 26 - 3
tests/Service/OnChange/Organization/OnSubdomainChangeTest.php

@@ -1,6 +1,6 @@
-<?php /** @noinspection DuplicatedCode */
+<?php
 
-namespace App\Test\Service\OnChange\Organization;
+namespace App\Tests\Service\OnChange\Organization;
 
 use App\Entity\Access\Access;
 use App\Entity\Organization\Organization;
@@ -13,7 +13,6 @@ use App\Service\OnChange\Organization\OnSubdomainChange;
 use App\Service\Organization\Utils as OrganizationUtils;
 use App\Service\Typo3\BindFileService;
 use Doctrine\Common\Collections\ArrayCollection;
-use Doctrine\ORM\EntityManagerInterface;
 use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\Messenger\Envelope;
@@ -43,6 +42,9 @@ class OnSubdomainChangeTest extends TestCase
             ->getMock();
     }
 
+    /**
+     * @see OnSubdomainChange::validate()
+     */
     public function testValidateIsOk(): void
     {
         $onSubdomainChange = $this->makeOnSubdomainChangeMock('validate');
@@ -59,6 +61,9 @@ class OnSubdomainChangeTest extends TestCase
         $onSubdomainChange->validate($subdomain, $context);
     }
 
+    /**
+     * @see OnSubdomainChange::validate()
+     */
     public function testValidateIsPutRequest(): void
     {
         $onSubdomainChange = $this->makeOnSubdomainChangeMock('validate');
@@ -72,6 +77,9 @@ class OnSubdomainChangeTest extends TestCase
         $onSubdomainChange->validate($subdomain, $context);
     }
 
+    /**
+     * @see OnSubdomainChange::validate()
+     */
     public function testValidateMaxReached(): void
     {
         $onSubdomainChange = $this->makeOnSubdomainChangeMock('validate');
@@ -89,6 +97,9 @@ class OnSubdomainChangeTest extends TestCase
         $onSubdomainChange->validate($subdomain, $context);
     }
 
+    /**
+     * @see OnSubdomainChange::beforeChange()
+     */
     public function testBeforeChangeActivated(): void
     {
         $onSubdomainChange = $this->makeOnSubdomainChangeMock('beforeChange');
@@ -119,6 +130,9 @@ class OnSubdomainChangeTest extends TestCase
         $onSubdomainChange->beforeChange($subdomain, $context);
     }
 
+    /**
+     * @see OnSubdomainChange::onChange()
+     */
     public function testOnChangeNoChange(): void
     {
         $onSubdomainChange = $this->makeOnSubdomainChangeMock('onChange');
@@ -133,6 +147,9 @@ class OnSubdomainChangeTest extends TestCase
         $onSubdomainChange->onChange($subdomain, $context);
     }
 
+    /**
+     * @see OnSubdomainChange::onChange()
+     */
     public function testOnChangeActivated(): void {
         $onSubdomainChange = $this->makeOnSubdomainChangeMock('onChange');
 
@@ -166,6 +183,9 @@ class OnSubdomainChangeTest extends TestCase
         $onSubdomainChange->onChange($subdomain, $context);
     }
 
+    /**
+     * @see OnSubdomainChange::onChange()
+     */
     public function testOnChangeCreated(): void {
         $onSubdomainChange = $this->makeOnSubdomainChangeMock('onChange');
 
@@ -195,6 +215,9 @@ class OnSubdomainChangeTest extends TestCase
         $onSubdomainChange->onChange($subdomain, $context);
     }
 
+    /**
+     * @see OnSubdomainChange::sendEmailAfterSubdomainChange()
+     */
     public function testSendEmailAfterSubdomainChange(): void {
         $onSubdomainChange = $this->makeOnSubdomainChangeMock('sendEmailAfterSubdomainChange');
 

+ 26 - 2
tests/Service/Organization/OrganizationProfileCreatorTest.php

@@ -1,6 +1,6 @@
-<?php /** @noinspection DuplicatedCode */
+<?php
 
-namespace App\Test\Service\Organization;
+namespace App\Tests\Service\Organization;
 
 use App\ApiResources\Profile\OrganizationProfile;
 use App\Entity\Network\Network;
@@ -114,6 +114,7 @@ class OrganizationProfileCreatorTest extends TestCase
 
     /**
      * If the organization principal type is ARTISTIC_EDUCATION_ONLY, then showAdherentList should be set to false
+     * @see OrganizationProfileCreator::createCompleteOrganizationProfile()
      */
     public function testCreateOrganizationProfileWithoutAdherentListBecauseOfPrincipalType(): void
     {
@@ -142,6 +143,7 @@ class OrganizationProfileCreatorTest extends TestCase
 
     /**
      * If the parameters::showAdherentList is false, then organizationProfile::showAdherentList should be set to false
+     * @see OrganizationProfileCreator::createCompleteOrganizationProfile()
      */
     public function testCreateOrganizationProfileWithoutAdherentList(): void
     {
@@ -166,4 +168,26 @@ class OrganizationProfileCreatorTest extends TestCase
 
         $organizationProfileCreator->createCompleteOrganizationProfile($organization);
     }
+
+    /**
+     * @see OrganizationProfileCreator::createLightOrganizationProfile()
+     */
+    public function testCreateLightOrganizationProfile(): void {
+        $organizationProfileCreator = $this->getMockBuilder(OrganizationProfileCreator::class)
+            ->setConstructorArgs([$this->module, $this->tree, $this->organizationUtils])
+            ->setMethodsExcept(['createLightOrganizationProfile'])
+            ->getMock();
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+
+        $organization->method('getId')->willReturn(123);
+        $organization->method('getName')->willReturn('Foo');
+        $this->organizationUtils->method('getOrganizationWebsite')->with($organization)->willReturn('foo.net');
+
+        $result = $organizationProfileCreator->createLightOrganizationProfile($organization);
+
+        $this->assertEquals(123, $result->getId());
+        $this->assertEquals('Foo', $result->getName());
+        $this->assertEquals('foo.net', $result->getWebsite());
+    }
 }

+ 180 - 9
tests/Service/Organization/UtilsTest.php

@@ -1,13 +1,16 @@
 <?php /** @noinspection PhpUnhandledExceptionInspection */
 
-namespace App\Test\Service\Organization;
+namespace App\Tests\Service\Organization;
 
 use App\Entity\Organization\Organization;
 use App\Entity\Organization\Parameters;
 use App\Entity\Organization\Settings;
+use App\Entity\Organization\Subdomain;
 use App\Enum\Organization\OrganizationIdsEnum;
 use App\Enum\Organization\SettingsProductEnum;
 use App\Service\Organization\Utils as OrganizationUtils;
+use App\Service\Utils\DatesUtils;
+use Doctrine\Common\Collections\ArrayCollection;
 use PHPUnit\Framework\TestCase;
 
 class TestableOrganizationUtils extends OrganizationUtils {
@@ -132,19 +135,25 @@ class UtilsTest extends TestCase
         $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationCurrentActivityYear'])->getMock();
 
         $parameters = $this->getMockBuilder(Parameters::class)->getMock();
-        $parameters->method('getMusicalDate')->willReturn(new \DateTime('2020-09-01'));
+        $parameters->method('getMusicalDate')->willReturnOnConsecutiveCalls(
+                null,  // default should be '2020-08-31'
+                new \DateTime('2020-10-01')
+            );
 
         $organization = $this->getMockBuilder(Organization::class)->getMock();
         $organization->method('getParameters')->willReturn($parameters);
 
-        $today = new \DateTime('now');
-        $expected = (int)$today->format('Y');
-        if ((int)$today->format('m') < 9) {
-            --$expected;
-        }
+        DatesUtils::setFakeDatetime('2020-03-01');
 
         $this->assertEquals(
-            $expected,
+            2019,
+            $organizationUtils->getOrganizationCurrentActivityYear($organization)
+        );
+
+        DatesUtils::setFakeDatetime('2020-11-01');
+
+        $this->assertEquals(
+            2020,
             $organizationUtils->getOrganizationCurrentActivityYear($organization)
         );
     }
@@ -168,10 +177,29 @@ class UtilsTest extends TestCase
         $this->assertEquals('2023-09-04', $periods['dateEnd']);
     }
 
+    /**
+     * @see OrganizationUtils::getActivityPeriodsSwitchYear()
+     */
+    public function testGetActivityPeriodsSwitchYearNoMusicalDate(): void
+    {
+        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getActivityPeriodsSwitchYear'])->getMock();
+
+        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
+        $parameters->method('getMusicalDate')->willReturn(null);
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->method('getParameters')->willReturn($parameters);
+
+        $periods = $organizationUtils->getActivityPeriodsSwitchYear($organization, 2022);
+
+        $this->assertEquals('2022-09-01', $periods['dateStart']);
+        $this->assertEquals('2023-08-31', $periods['dateEnd']);
+    }
+
     /**
      * @see OrganizationUtils::getActivityYearSwitchDate()
      */
-    public function testgetActivityYearSwitchDate(): void
+    public function testGetActivityYearSwitchDate(): void
     {
         $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getActivityYearSwitchDate'])->getMock();
 
@@ -184,4 +212,147 @@ class UtilsTest extends TestCase
         $this->assertEquals(2022, $organizationUtils->getActivityYearSwitchDate($organization, new \DateTime('2022-09-10')));
         $this->assertEquals(2021, $organizationUtils->getActivityYearSwitchDate($organization, new \DateTime('2022-09-02')));
     }
+
+    /**
+     * @see OrganizationUtils::getActivityYearSwitchDate()
+     */
+    public function testGetActivityYearSwitchDateNoMusicalDate(): void
+    {
+        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getActivityYearSwitchDate'])->getMock();
+
+        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
+        $parameters->method('getMusicalDate')->willReturn(null);
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->method('getParameters')->willReturn($parameters);
+
+        $this->assertEquals(2021, $organizationUtils->getActivityYearSwitchDate($organization, new \DateTime('2022-08-01')));
+        $this->assertEquals(2022, $organizationUtils->getActivityYearSwitchDate($organization, new \DateTime('2022-09-02')));
+    }
+
+    /**
+     * @see OrganizationUtils::getOrganizationActiveSubdomain()
+     */
+    public function testGetOrganizationActiveSubdomain(): void {
+        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationActiveSubdomain'])->getMock();
+
+        $subdomain1 = $this->getMockBuilder(Subdomain::class)->getMock();
+        $subdomain1->method('isActive')->willReturn(false);
+
+        $subdomain2 = $this->getMockBuilder(Subdomain::class)->getMock();
+        $subdomain2->method('isActive')->willReturn(false);
+
+        $subdomain3 = $this->getMockBuilder(Subdomain::class)->getMock();
+        $subdomain3->method('isActive')->willReturn(true);
+        $subdomain3->method('getSubdomain')->willReturn('foo');
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->method('getSubdomains')->willReturn(new ArrayCollection([$subdomain1, $subdomain2, $subdomain3]));
+
+        $this->assertEquals('foo', $organizationUtils->getOrganizationActiveSubdomain($organization));
+    }
+
+    /**
+     * @see OrganizationUtils::getOrganizationActiveSubdomain()
+     */
+    public function testGetOrganizationActiveSubdomainNone(): void {
+        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationActiveSubdomain'])->getMock();
+
+        $subdomain1 = $this->getMockBuilder(Subdomain::class)->getMock();
+        $subdomain1->method('isActive')->willReturn(false);
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->method('getSubdomains')->willReturn(new ArrayCollection([$subdomain1]));
+
+        $this->assertEquals(null, $organizationUtils->getOrganizationActiveSubdomain($organization));
+    }
+
+    /**
+     * @see OrganizationUtils::getOrganizationWebsite()
+     */
+    public function testOrganizationWebsite(): void
+    {
+        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationWebsite'])->getMock();
+
+        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
+        $parameters->method('getDesactivateOpentalentSiteWeb')->willReturn(false);
+        $parameters->method('getCustomDomain')->willReturn(null);
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->method('getParameters')->willReturn($parameters);
+
+        $organizationUtils->method('getOrganizationActiveSubdomain')->with($organization)->willReturn('foo');
+
+        $this->assertEquals('https://foo.opentalent.fr', $organizationUtils->getOrganizationWebsite($organization));
+    }
+
+    /**
+     * @see OrganizationUtils::getOrganizationWebsite()
+     */
+    public function testOrganizationWebsiteDeactivatedWithOther(): void
+    {
+        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationWebsite'])->getMock();
+
+        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
+        $parameters->method('getDesactivateOpentalentSiteWeb')->willReturn(true);
+        $parameters->method('getOtherWebsite')->willReturn('foo.net');
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->method('getParameters')->willReturn($parameters);
+
+        $this->assertEquals('https://foo.net', $organizationUtils->getOrganizationWebsite($organization));
+    }
+
+    /**
+     * @see OrganizationUtils::getOrganizationWebsite()
+     */
+    public function testOrganizationWebsiteDeactivatedNoOther(): void
+    {
+        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationWebsite'])->getMock();
+
+        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
+        $parameters->method('getDesactivateOpentalentSiteWeb')->willReturn(true);
+        $parameters->method('getOtherWebsite')->willReturn(null);
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->method('getParameters')->willReturn($parameters);
+
+        $this->assertEquals(null, $organizationUtils->getOrganizationWebsite($organization));
+    }
+
+    /**
+     * @see OrganizationUtils::getOrganizationWebsite()
+     */
+    public function testOrganizationWebsiteWithCustom(): void
+    {
+        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationWebsite'])->getMock();
+
+        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
+        $parameters->method('getDesactivateOpentalentSiteWeb')->willReturn(false);
+        $parameters->method('getCustomDomain')->willReturn('bar.net');
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->method('getParameters')->willReturn($parameters);
+
+        $this->assertEquals('https://bar.net', $organizationUtils->getOrganizationWebsite($organization));
+    }
+
+    /**
+     * @see OrganizationUtils::getOrganizationWebsite()
+     */
+    public function testOrganizationWebsiteNoSubdomains(): void
+    {
+        $organizationUtils = $this->getMockBuilder(TestableOrganizationUtils::class)->setMethodsExcept(['getOrganizationWebsite'])->getMock();
+
+        $parameters = $this->getMockBuilder(Parameters::class)->getMock();
+        $parameters->method('getDesactivateOpentalentSiteWeb')->willReturn(false);
+        $parameters->method('getCustomDomain')->willReturn(null);
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->method('getParameters')->willReturn($parameters);
+
+        $organizationUtils->method('getOrganizationActiveSubdomain')->with($organization)->willReturn(null);
+
+        $this->assertEquals(null, $organizationUtils->getOrganizationWebsite($organization));
+    }
 }

+ 28 - 3
tests/Service/Rest/ApiRequestServiceTest.php

@@ -1,10 +1,8 @@
 <?php
 
-namespace App\Tests\Service;
+namespace App\Tests\Service\Rest;
 
 use App\Service\Rest\ApiRequestService;
-use AssertionError;
-use Elastica\Transport\Http;
 use PHPUnit\Framework\TestCase;
 use Symfony\Component\HttpClient\Exception\TransportException;
 use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -19,6 +17,9 @@ class ApiRequestServiceTest extends TestCase
         $this->client = $this->getMockBuilder(HttpClientInterface::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see ApiRequestService::getJsonContent()
+     */
     public function testGetJsonContent(): void
     {
         $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
@@ -36,6 +37,9 @@ class ApiRequestServiceTest extends TestCase
         $this->assertEquals(['foo' => 'bar'], $data);
     }
 
+    /**
+     * @see ApiRequestService::getJsonContent()
+     */
     public function testGetContent(): void
     {
         $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
@@ -56,6 +60,9 @@ class ApiRequestServiceTest extends TestCase
         $this->assertEquals('{foo: bar}', $content);
     }
 
+    /**
+     * @see ApiRequestService::getContent()
+     */
     public function testGetContentWithError(): void
     {
         $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
@@ -71,6 +78,9 @@ class ApiRequestServiceTest extends TestCase
         $apiRequestService->getContent('path/to/data');
     }
 
+    /**
+     * @see ApiRequestService::get()
+     */
     public function testGet(): void
     {
         $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
@@ -90,6 +100,9 @@ class ApiRequestServiceTest extends TestCase
         $this->assertEquals($response, $actualResponse);
     }
 
+    /**
+     * @see ApiRequestService::post()
+     */
     public function testPost(): void
     {
         $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
@@ -109,6 +122,9 @@ class ApiRequestServiceTest extends TestCase
         $this->assertEquals($response, $actualResponse);
     }
 
+    /**
+     * @see ApiRequestService::put()
+     */
     public function testPut(): void
     {
         $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
@@ -128,6 +144,9 @@ class ApiRequestServiceTest extends TestCase
         $this->assertEquals($response, $actualResponse);
     }
 
+    /**
+     * @see ApiRequestService::delete()
+     */
     public function testDelete(): void
     {
         $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
@@ -147,6 +166,9 @@ class ApiRequestServiceTest extends TestCase
         $this->assertEquals($response, $actualResponse);
     }
 
+    /**
+     * @see ApiRequestService::request()
+     */
     public function testRequest(): void
     {
         $apiRequestService = $this->getMockBuilder(ApiRequestService::class)
@@ -162,6 +184,9 @@ class ApiRequestServiceTest extends TestCase
         $this->assertEquals($response, $actualResponse);
     }
 
+    /**
+     * @see ApiRequestService::request()
+     */
     public function testRequestWithError(): void
     {
         $apiRequestService = $this->getMockBuilder(ApiRequestService::class)

+ 24 - 4
tests/Service/Rest/Operation/BaseRestOperationTest.php

@@ -1,5 +1,7 @@
 <?php
 
+namespace App\Tests\Service\Rest\Operation;
+
 use App\Service\Rest\ApiRequestService;
 use App\Service\Rest\Operation\BaseRestOperation;
 use PHPUnit\Framework\TestCase;
@@ -20,6 +22,16 @@ class BaseRestOperationTest extends TestCase
             ->getMock();
     }
 
+    /**
+     * @see BaseRestOperation::__construct()
+     * @see BaseRestOperation::getLabel()
+     * @see BaseRestOperation::getMethod()
+     * @see BaseRestOperation::getPath()
+     * @see BaseRestOperation::getInitialData()
+     * @see BaseRestOperation::getParameters()
+     * @see BaseRestOperation::getOptions()
+     * @see BaseRestOperation::__toString()
+     */
     public function testGetters(): void
     {
         $operation = new TestableBaseRestOperation(
@@ -43,6 +55,9 @@ class BaseRestOperationTest extends TestCase
 
     /**
      * Test execution with a valid request
+     * @see BaseRestOperation::execute()
+     * @see BaseRestOperation::getStatus()
+     * @see BaseRestOperation::getErrorMessage()
      */
     public function testExecuteValid(): void
     {
@@ -68,6 +83,9 @@ class BaseRestOperationTest extends TestCase
 
     /**
      * Test execution with an invalid request (api returns an error 404 for example)
+     * @see BaseRestOperation::execute()
+     * @see BaseRestOperation::getStatus()
+     * @see BaseRestOperation::getErrorMessage()
      */
     public function testExecuteInvalid(): void
     {
@@ -88,14 +106,16 @@ class BaseRestOperationTest extends TestCase
 
         try {
             $operation->execute($this->apiRequestService);
-        } catch (RuntimeException) {
-        }
+        } catch (\RuntimeException) {}
         $this->assertEquals(BaseRestOperation::STATUS_ERROR, $operation->getStatus());
-        $this->assertMatchesRegularExpression('/.*Not found.*/', $operation->getErrorMessage());
+        self::assertMatchesRegularExpression('/.*Not found.*/', $operation->getErrorMessage());
     }
 
     /**
      * Test execution if the request throw an HTTP exception
+     * @see BaseRestOperation::execute()
+     * @see BaseRestOperation::getStatus()
+     * @see BaseRestOperation::getErrorMessage()
      */
     public function testExecutionError(): void
     {
@@ -121,7 +141,7 @@ class BaseRestOperationTest extends TestCase
 
         try {
             $operation->execute($this->apiRequestService);
-        } catch (RuntimeException) {
+        } catch (\RuntimeException) {
         }
         $this->assertEquals(BaseRestOperation::STATUS_ERROR, $operation->getStatus());
         $this->assertMatchesRegularExpression(

+ 13 - 0
tests/Service/Rest/Operation/CreateOperationTest.php

@@ -1,10 +1,20 @@
 <?php
 
+namespace App\Tests\Service\Rest\Operation;
+
 use App\Service\Rest\Operation\CreateOperation;
 use PHPUnit\Framework\TestCase;
 
 class CreateOperationTest extends TestCase
 {
+    /**
+     * @see CreateOperation::__construct()
+     * @see CreateOperation::getMethod()
+     * @see CreateOperation::getEntityName()
+     * @see CreateOperation::getPath()
+     * @see CreateOperation::getData()
+     * @see CreateOperation::__toString()
+     */
     public function testGetters(): void
     {
         $operation = new CreateOperation(
@@ -21,6 +31,9 @@ class CreateOperationTest extends TestCase
         $this->assertEquals('POST dinosaur', (string)$operation);
     }
 
+    /**
+     * @see CreateOperation::getChangeLog()
+     */
     public function testGetChangeLog(): void
     {
         $operation = new CreateOperation(

+ 33 - 3
tests/Service/Rest/Operation/DeleteOperationTest.php

@@ -1,12 +1,25 @@
 <?php
 
-use App\Service\Rest\Operation\CreateOperation;
+namespace App\Tests\Service\Rest\Operation;
+
 use App\Service\Rest\Operation\DeleteOperation;
 use PHPUnit\Framework\TestCase;
 
+class TestableDeleteOperation extends DeleteOperation {
+    public function getExpectedResult(): ?array { return parent::getExpectedResult(); }
+}
+
 class DeleteOperationTest extends TestCase
 {
-    public function testGetters() {
+    /**
+     * @see DeleteOperation::__construct()
+     * @see DeleteOperation::getMethod()
+     * @see DeleteOperation::getEntityName()
+     * @see DeleteOperation::getPath()
+     * @see DeleteOperation::__toString()
+     */
+    public function testGetters(): void
+    {
         $operation = new DeleteOperation(
             'Delete a dinosaur',
             'dinosaur',
@@ -20,7 +33,24 @@ class DeleteOperationTest extends TestCase
         $this->assertEquals('DELETE dinosaur/1', (string)$operation);
     }
 
-    public function testGetChangeLog() {
+    /**
+     * @see DeleteOperation::getExpectedResult()
+     */
+    public function testGetExpectedResult(): void {
+        $operation = new TestableDeleteOperation(
+            'Delete a dinosaur',
+            'dinosaur',
+            1
+        );
+
+        $this->assertEquals(null, $operation->getExpectedResult());
+    }
+
+    /**
+     * @see DeleteOperation::getChangeLog()
+     */
+    public function testGetChangeLog(): void
+    {
         $operation = new DeleteOperation(
             'Delete a dinosaur',
             'dinosaur',

+ 18 - 3
tests/Service/Rest/Operation/UpdateOperationTest.php

@@ -1,12 +1,23 @@
 <?php
 
-use App\Service\Rest\Operation\CreateOperation;
+namespace App\Tests\Service\Rest\Operation;
+
 use App\Service\Rest\Operation\UpdateOperation;
 use PHPUnit\Framework\TestCase;
 
 class UpdateOperationTest extends TestCase
 {
-    public function testGetters() {
+    /**
+     * @see UpdateOperation::__construct()
+     * @see UpdateOperation::getMethod()
+     * @see UpdateOperation::getEntityName()
+     * @see UpdateOperation::getPath()
+     * @see UpdateOperation::getInitialData()
+     * @see UpdateOperation::getData()
+     * @see UpdateOperation::__toString()
+     */
+    public function testGetters(): void
+    {
         $operation = new UpdateOperation(
             'Update a dinosaur',
             'dinosaur',
@@ -24,7 +35,11 @@ class UpdateOperationTest extends TestCase
         $this->assertEquals('PUT dinosaur/1', (string)$operation);
     }
 
-    public function testGetChangeLog() {
+    /**
+     * @see UpdateOperation::getChangeLog()
+     */
+    public function testGetChangeLog(): void
+    {
         $operation = new UpdateOperation(
             'Update a dinosaur',
             'dinosaur',

+ 245 - 40
tests/Service/Security/ModuleTest.php

@@ -1,32 +1,44 @@
 <?php
-namespace App\Test\Service\Security;
+
+namespace App\Tests\Service\Security;
 
 use App\Entity\Organization\Organization;
 use App\Entity\Organization\Settings;
+use App\Service\Cotisation\Utils as CotisationUtils;
+use App\Service\Security\Module;
 use App\Service\Utils\Parser;
 use App\Service\Utils\Reflection;
-use Doctrine\Common\Collections\ArrayCollection;
+use PHPUnit\Framework\MockObject\MockObject;
 use PHPUnit\Framework\TestCase;
-use App\Service\Security\Module;
-use App\Service\Cotisation\Utils as CotisationUtils;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+
+class TestableModule extends Module {
+    public function loadModuleConfig(): array { return parent::loadModuleConfig(); }
+    public function getModuleConfig(): array { return parent::getModuleConfig(); }
+    public function loadModuleByConditionsConfig(): array { return parent::loadModuleByConditionsConfig(); }
+    public function getModuleByConditionsConfig(): array { return parent::getModuleByConditionsConfig(); }
+}
 
 class ModuleTest extends TestCase
 {
     private const OPENTALENT_CONFIG = __DIR__.'/../../../config/opentalent';
 
-    private Reflection $reflectionMock;
-    private Parser $parser;
+    private MockObject | Reflection $reflection;
+    private MockObject | Parser $parser;
 
     public function setUp():void
     {
-        $this->reflectionMock = $this->getMockBuilder(Reflection::class)->disableOriginalConstructor()->getMock();
-        $this->parser = new Parser();
+        $this->reflection = $this->getMockBuilder(Reflection::class)->disableOriginalConstructor()->getMock();
+        $this->parser = $this->getMockBuilder(Parser::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see Module::getOrganizationModules()
+     */
     public function testGetOrganizationModules(): void
     {
         $module = $this->getMockBuilder(Module::class)
-            ->setConstructorArgs([$this->reflectionMock, $this->parser, self::OPENTALENT_CONFIG])
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
             ->setMethodsExcept(['getOrganizationModules'])
             ->getMock();
 
@@ -52,26 +64,24 @@ class ModuleTest extends TestCase
      */
     public function testGetModuleBySettings(): void
     {
-        $module = $this->getMockBuilder(Module::class)
-            ->setConstructorArgs([$this->reflectionMock, $this->parser, self::OPENTALENT_CONFIG])
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
             ->setMethodsExcept(['getModuleBySettings'])
             ->getMock();
 
-        $settingsMock = $this->getMockBuilder(Settings::class)->getMock();
-        $settingsMock
+        $settings = $this->getMockBuilder(Settings::class)->getMock();
+        $settings
             ->expects($this->once())
             ->method('getModules')
             ->willReturn(["Sms" => true]);
 
-        $organizationMock = $this->getMockBuilder(Organization::class)->getMock();
-        $organizationMock
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization
             ->expects($this->once())
             ->method('getSettings')
-            ->willReturn($settingsMock);
+            ->willReturn($settings);
 
-        $value = "Sms";
-        // assert function to test whether 'value' is a value of array
-        $this->assertContains($value, $module->getModuleBySettings($organizationMock)) ;
+        $this->assertContains('Sms', $module->getModuleBySettings($organization)) ;
     }
 
     /**
@@ -79,39 +89,234 @@ class ModuleTest extends TestCase
      */
     public function testGetModulesByConditions(): void
     {
-        $module = $this->getMockBuilder(Module::class)
-            ->setConstructorArgs([$this->reflectionMock, $this->parser, self::OPENTALENT_CONFIG])
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
             ->setMethodsExcept(['getModulesByConditions'])
             ->getMock();
 
-        $organizationMock = $this->getMockBuilder(Organization::class)->getMock();
-        $this->reflectionMock
+        $module->method('getModuleByConditionsConfig')->willReturn(
+            ['opentalent' =>
+                ['modulesbyconditions' =>
+                    ['CotisationCall' => [
+                        'roles' => ['ROLE_COTISATION'],
+                        'conditions' => [
+                            'service' => [
+                                'name' => CotisationUtils::class,
+                                'function' => 'isLastParentAndCMF'
+                            ]
+                        ]
+                    ]]
+                ]
+            ]
+        );
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $this->reflection
+            ->method('dynamicInvokeServiceWithArgsAndMethod')
+            ->with(CotisationUtils::class, 'isLastParentAndCMF', array($organization))
+            ->willReturn(true);
+
+        $this->assertContains('CotisationCall', $module->getModulesByConditions($organization)) ;
+    }
+
+    /**
+     * @see Module::getModulesByConditions()
+     */
+    public function testGetModulesByConditionsLogicError(): void
+    {
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
+            ->setMethodsExcept(['getModulesByConditions'])
+            ->getMock();
+
+        $module->method('getModuleByConditionsConfig')->willReturn(
+            ['opentalent' =>
+                ['modulesbyconditions' =>
+                    ['CotisationCall' => []]
+                ]
+            ]
+        );
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $this->reflection
             ->method('dynamicInvokeServiceWithArgsAndMethod')
-            ->withConsecutive(
-                [CotisationUtils::class, 'isLastParentAndCMF', array($organizationMock)]
-            )
-            ->willReturnOnConsecutiveCalls(
-                [true]
-            )
-        ;
-
-        $value = "CotisationCall";
-        // assert function to test whether 'value' is a value of array
-        $this->assertContains($value, $module->getModulesByConditions($organizationMock)) ;
+            ->with(CotisationUtils::class, 'isLastParentAndCMF', array($organization))
+            ->willThrowException(new \Exception());
+
+        $this->expectException(\LogicException::class);
+
+        $module->getModulesByConditions($organization);
     }
 
     /**
      * @see Module::getModulesByProductConfiguration()
      */
-    public function testGetModulesByProductConfiguration()
+    public function testGetModulesByProductConfiguration(): void
     {
-        $module = $this->getMockBuilder(Module::class)
-            ->setConstructorArgs([$this->reflectionMock, $this->parser, self::OPENTALENT_CONFIG])
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
+            ->setMethodsExcept(['getModulesByProductConfiguration'])
+            ->getMock();
+
+        $module->method('getModuleConfig')->willReturn(['opentalent' => ['products' => ['artist' => ['modules' => ['foo']]]]]);
+
+        $this->assertEquals(['foo'], $module->getModulesByProductConfiguration('artist')) ;
+    }
+
+    /**
+     * @see Module::getModulesByProductConfiguration()
+     */
+    public function testGetModulesByProductConfigurationExtend(): void
+    {
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
+            ->setMethodsExcept(['getModulesByProductConfiguration'])
+            ->getMock();
+
+        $module->method('getModuleConfig')->willReturn(
+            ['opentalent' =>
+                ['products' =>
+                    [
+                        'artist' => ['modules' => ['foo']],
+                        'artist_premium' => ['extend' => 'artist', 'modules' => ['bar']]
+                    ]
+                ]
+            ]
+        );
+
+        $this->assertEqualsCanonicalizing(
+            ['foo', 'bar'],
+            $module->getModulesByProductConfiguration('artist-premium')
+        ) ;
+    }
+
+    /**
+     * @see Module::getModulesByProductConfiguration()
+     */
+    public function testGetModulesByProductConfigurationAccessDenied(): void: void
+    {
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
             ->setMethodsExcept(['getModulesByProductConfiguration'])
             ->getMock();
 
-        $value = "MessagesAdvanced";
-        // assert function to test whether 'value' is a value of array
-        $this->assertContains($value, $module->getModulesByProductConfiguration('artist-premium')) ;
+        $module->method('getModuleConfig')->willReturn(['opentalent' => ['products' => ['artist' => ['modules' => ['foo']]]]]);
+
+        $this->expectException(AccessDeniedHttpException::class);
+        $this->expectExceptionMessage('The product artist_premium does not exist !');
+
+        $module->getModulesByProductConfiguration('artist-premium');
+    }
+
+    /**
+     * @see Module::getModuleConfig()
+     */
+    public function testGetModuleConfig(): void {
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
+            ->setMethodsExcept(['getModuleConfig'])
+            ->getMock();
+
+        $module->expects(self::once())->method('loadModuleConfig')->willReturn(['foo']);
+
+        $this->assertEquals(['foo'], $module->getModuleConfig());
+        $this->assertEquals(['foo'], $module->getModuleConfig());
+    }
+
+    /**
+     * @see Module::loadModuleConfig()
+     */
+    public function testLoadModuleConfig(): void {
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
+            ->setMethodsExcept(['loadModuleConfig'])
+            ->getMock();
+
+        $this->parser
+            ->expects(self::once())
+            ->method('yamlParser')
+            ->with(self::OPENTALENT_CONFIG, 'products.yaml')
+            ->willReturn(['foo']);
+
+        $this->assertEquals(
+            ['foo'],
+            $module->loadModuleConfig()
+        );
+    }
+
+    /**
+     * @see Module::getModuleByConditionsConfig()
+     */
+    public function testGetModuleByConditionsConfig(): void {
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
+            ->setMethodsExcept(['getModuleByConditionsConfig'])
+            ->getMock();
+
+        $module->expects(self::once())->method('loadModuleByConditionsConfig')->willReturn(['foo']);
+
+        $this->assertEquals(['foo'], $module->getModuleByConditionsConfig());
+        $this->assertEquals(['foo'], $module->getModuleByConditionsConfig());
+    }
+
+    /**
+     * @see Module::loadModuleByConditionsConfig()
+     */
+    public function testLoadModuleByConditionsConfig(): void {
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
+            ->setMethodsExcept(['loadModuleByConditionsConfig'])
+            ->getMock();
+
+        $this->parser->expects(self::once())
+            ->method('yamlParser')
+            ->with(self::OPENTALENT_CONFIG, 'modulesbyconditions.yaml')
+            ->willReturn(['foo']);
+
+        $this->assertEquals(
+            ['foo'],
+            $module->loadModuleByConditionsConfig()
+        );
+    }
+
+    /**
+     * @see Module::getModuleByResourceName()
+     */
+    public function testGetModuleByResourceName(): void {
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
+            ->setMethodsExcept(['getModuleByResourceName'])
+            ->getMock();
+
+
+        $module->method('getModuleConfig')->willReturn(
+            ['opentalent' =>
+                ['modules' =>
+                    ['Core' => ['entities' => ['foo', 'bar']]]
+                ]
+            ]
+        );
+
+        $this->assertEquals('Core', $module->getModuleByResourceName('foo'));
+    }
+
+    /**
+     * @see Module::getModuleByResourceName()
+     */
+    public function testGetModuleByResourceNameNotFound(): void {
+        $module = $this->getMockBuilder(TestableModule::class)
+            ->setConstructorArgs([$this->reflection, $this->parser, self::OPENTALENT_CONFIG])
+            ->setMethodsExcept(['getModuleByResourceName'])
+            ->getMock();
+
+        $module->method('getModuleConfig')->willReturn(
+            ['opentalent' =>
+                ['modules' =>
+                    ['Core' => ['entities' => ['bar']]]
+                ]
+            ]
+        );
+
+        $this->assertNull($module->getModuleByResourceName('foo'));
     }
 }

+ 2 - 1
tests/Service/Security/SwitchUserTest.php

@@ -1,5 +1,6 @@
 <?php
-namespace App\Test\Service\Security;
+
+namespace App\Tests\Service\Security;
 
 use App\Entity\Access\Access;
 use App\Service\Security\SwitchUser;

+ 8 - 3
tests/Service/ServiceIterator/CurrentAccessExtensionIteratorTest.php

@@ -1,5 +1,7 @@
 <?php
 
+namespace App\Tests\Service\ServiceIterator;
+
 use App\Doctrine\Access\AccessExtensionInterface;
 use App\Service\ServiceIterator\CurrentAccessExtensionIterator;
 use Doctrine\ORM\QueryBuilder;
@@ -7,6 +9,9 @@ use PHPUnit\Framework\TestCase;
 
 class CurrentAccessExtensionIteratorTest extends TestCase
 {
+    /**
+     * @see CurrentAccessExtensionIterator::addWhere()
+     */
     public function testAddWhere(): void
     {
         $queryBuilder = $this->getMockBuilder(QueryBuilder::class)
@@ -17,6 +22,7 @@ class CurrentAccessExtensionIteratorTest extends TestCase
 
         $ext1 = $mocker->getMock();
         $ext1->method('support')->willReturn(false);
+        $ext1->expects($this->never())->method('addWhere')->with($queryBuilder)->willReturn(true);
 
         $ext2 = $mocker->getMock();
         $ext2->method('support')->with('foo')->willReturn(true);
@@ -24,6 +30,7 @@ class CurrentAccessExtensionIteratorTest extends TestCase
 
         $ext3 = $mocker->getMock();
         $ext3->method('support')->willReturn(false);
+        $ext1->expects($this->never())->method('addWhere')->with($queryBuilder)->willReturn(true);
 
         $extensions = [$ext1, $ext2, $ext3];
 
@@ -32,8 +39,6 @@ class CurrentAccessExtensionIteratorTest extends TestCase
             ->setMethodsExcept(['addWhere'])
             ->getMock();
 
-        $actualExt = $iterator->addWhere($queryBuilder, 'foo');
-
-        $this->assertEquals(true, $actualExt);
+        $iterator->addWhere($queryBuilder, 'foo');
     }
 }

+ 9 - 0
tests/Service/ServiceIterator/EncoderIteratorTest.php

@@ -1,11 +1,17 @@
 <?php
 
+namespace App\Tests\Service\ServiceIterator;
+
 use App\Service\Export\Encoder\EncoderInterface;
 use App\Service\ServiceIterator\EncoderIterator;
+use Exception;
 use PHPUnit\Framework\TestCase;
 
 class EncoderIteratorTest extends TestCase
 {
+    /**
+     * @see EncoderIterator::getEncoderFor()
+     */
     public function testGetEncoderFor(): void
     {
         $mocker = $this->getMockBuilder(EncoderInterface::class);
@@ -31,6 +37,9 @@ class EncoderIteratorTest extends TestCase
         $this->assertEquals($encoder2, $actualEncoder);
     }
 
+    /**
+     * @see EncoderIterator::getEncoderFor()
+     */
     public function testGetEncoderForError(): void
     {
         $mocker = $this->getMockBuilder(EncoderInterface::class);

+ 9 - 2
tests/Service/ServiceIterator/ExporterIteratorTest.php

@@ -1,14 +1,18 @@
 <?php
 
+namespace App\Tests\Service\ServiceIterator;
+
 use App\ApiResources\Export\ExportRequest;
-use App\Service\Export\Encoder\EncoderInterface;
 use App\Service\Export\ExporterInterface;
-use App\Service\ServiceIterator\EncoderIterator;
 use App\Service\ServiceIterator\ExporterIterator;
+use Exception;
 use PHPUnit\Framework\TestCase;
 
 class ExporterIteratorTest extends TestCase
 {
+    /**
+     * @see ExporterIterator::getExporterFor()
+     */
     public function testGetExporterFor(): void
     {
         $exportRequest = $this->getMockBuilder(ExportRequest::class)
@@ -38,6 +42,9 @@ class ExporterIteratorTest extends TestCase
         $this->assertEquals($exporter2, $actualExporter);
     }
 
+    /**
+     * @see ExporterIterator::getExporterFor()
+     */
     public function testGetExporterForError(): void
     {
         $exportRequest = $this->getMockBuilder(ExportRequest::class)

+ 10 - 4
tests/Service/ServiceIterator/OptionalsRolesIteratorTest.php

@@ -1,5 +1,7 @@
 <?php
 
+namespace App\Tests\Service\ServiceIterator;
+
 use App\Entity\Access\Access;
 use App\Service\Access\OptionalsRolesInterface;
 use App\Service\ServiceIterator\OptionalsRolesIterator;
@@ -7,7 +9,10 @@ use PHPUnit\Framework\TestCase;
 
 class OptionalsRolesIteratorTest extends TestCase
 {
-    public function testAddWhere(): void
+    /**
+     * @see OptionalsRolesIterator::getOptionalsRoles()
+     */
+    public function testGetOptionalsRoles(): void
     {
         $access = $this->getMockBuilder(Access::class)->getMock();
 
@@ -37,11 +42,12 @@ class OptionalsRolesIteratorTest extends TestCase
         $actualRoles = $iterator->getOptionalsRoles($access);
 
         $this->assertEquals(['ROLE2', 'ROLE4'], $actualRoles);
-
-
     }
 
-    public function testAddWhereNotFound(): void
+    /**
+     * @see OptionalsRolesIterator::getOptionalsRoles()
+     */
+    public function testGetOptionalsRolesNotFound(): void
     {
         $access = $this->getMockBuilder(Access::class)->getMock();
 

+ 2 - 0
tests/Service/Storage/ApiLegacyStorageTest.php

@@ -1,5 +1,7 @@
 <?php
 
+namespace App\Tests\Service\Storage;
+
 use App\Entity\Core\File;
 use App\Service\ApiLegacy\ApiLegacyRequestService;
 use App\Service\Storage\ApiLegacyStorage;

+ 78 - 3
tests/Service/Storage/LocalStorageTest.php

@@ -1,4 +1,6 @@
-<?php /** @noinspection DuplicatedCode */
+<?php
+
+namespace App\Tests\Service\Storage;
 
 use ApiPlatform\Core\Api\IriConverterInterface;
 use App\ApiResources\DownloadRequest;
@@ -10,11 +12,13 @@ use App\Enum\Core\FileStatusEnum;
 use App\Enum\Core\FileTypeEnum;
 use App\Repository\Access\AccessRepository;
 use App\Service\Storage\LocalStorage;
+use DateTime;
 use Doctrine\ORM\EntityManagerInterface;
 use Gaufrette\Filesystem;
 use JetBrains\PhpStorm\Pure;
 use Knp\Bundle\GaufretteBundle\FilesystemMap;
 use PHPUnit\Framework\TestCase;
+use RuntimeException;
 
 class TestableLocalStorage extends LocalStorage {
     public const FS_KEY = parent::FS_KEY;
@@ -47,6 +51,9 @@ class LocalStorageTest extends TestCase
         $this->filesystemMap->method('get')->with(TestableLocalStorage::FS_KEY)->willReturn($this->filesystem);
     }
 
+    /**
+     * @see LocalStorage::exists()
+     */
     public function testExists(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -61,6 +68,9 @@ class LocalStorageTest extends TestCase
         $this->assertTrue($fileStorage->exists($file));
     }
 
+    /**
+     * @see LocalStorage::exists()
+     */
     public function testExistsInexistant(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -75,6 +85,9 @@ class LocalStorageTest extends TestCase
         $this->assertFalse($fileStorage->exists($file));
     }
 
+    /**
+     * @see LocalStorage::getDownloadIri()
+     */
     public function testGetDownloadIri(): void
     {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
@@ -97,6 +110,9 @@ class LocalStorageTest extends TestCase
         );
     }
 
+    /**
+     * @see LocalStorage::listByOwner()
+     */
     public function testListByOwner(): void
     {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
@@ -116,6 +132,9 @@ class LocalStorageTest extends TestCase
         );
     }
 
+    /**
+     * @see LocalStorage::read()
+     */
     public function testRead(): void
     {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
@@ -134,6 +153,9 @@ class LocalStorageTest extends TestCase
         );
     }
 
+    /**
+     * @see LocalStorage::prepareFile()
+     */
     public function testPrepareFile(): void
     {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
@@ -172,6 +194,9 @@ class LocalStorageTest extends TestCase
 
     }
 
+    /**
+     * @see LocalStorage::prepareFile()
+     */
     public function testPrepareFileDefaultValues(): void
     {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
@@ -198,6 +223,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals('text/plain', $file->getMimeType());
     }
 
+    /**
+     * @see LocalStorage::prepareFile()
+     */
     public function testPrepareFileNoFlush(): void
     {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
@@ -225,6 +253,9 @@ class LocalStorageTest extends TestCase
         );
     }
 
+    /**
+     * @see LocalStorage::writeFile()
+     */
     public function testWriteFileNewFile(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -276,6 +307,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals($file, $returned);
     }
 
+    /**
+     * @see LocalStorage::writeFile()
+     */
     public function testWriteFileExistingFile(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -325,6 +359,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals($file, $returned);
     }
 
+    /**
+     * @see LocalStorage::writeFile()
+     */
     public function testWriteFileExistingButMissingFile(): void
     {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
@@ -353,9 +390,12 @@ class LocalStorageTest extends TestCase
         $this->expectException(RuntimeException::class);
         $this->expectDeprecationMessage('The file `' . $key . '` does not exist in the file storage');
 
-        $returned = $fileStorage->writeFile($file, '12346', $author);
+        $fileStorage->writeFile($file, '12346', $author);
     }
 
+    /**
+     * @see LocalStorage::writeFile()
+     */
     public function testWriteFileWithAccessOwner(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -395,7 +435,9 @@ class LocalStorageTest extends TestCase
         $fileStorage->writeFile($file, $content, $author);
     }
 
-
+    /**
+     * @see LocalStorage::writeFile()
+     */
     public function testWriteFileWithNoName(): void
     {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
@@ -414,6 +456,9 @@ class LocalStorageTest extends TestCase
         $fileStorage->writeFile($file, '...', $author);
     }
 
+    /**
+     * @see LocalStorage::makeFile()
+     */
     public function testMakeFile(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -447,6 +492,9 @@ class LocalStorageTest extends TestCase
             'mime/type');
     }
 
+    /**
+     * @see LocalStorage::delete()
+     */
     public function testDelete(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -470,6 +518,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals($file, $returned);
     }
 
+    /**
+     * @see LocalStorage::delete()
+     */
     public function testDeleteFailed(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -494,6 +545,9 @@ class LocalStorageTest extends TestCase
         $fileStorage->delete($file, $author);
     }
 
+    /**
+     * @see LocalStorage::getPrefix()
+     */
     public function testGetPrefixAccess(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -512,6 +566,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals('organization/2/1', $prefix);
     }
 
+    /**
+     * @see LocalStorage::getPrefix()
+     */
     public function testGetPrefixOrganization(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -526,6 +583,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals('organization/1', $prefix);
     }
 
+    /**
+     * @see LocalStorage::getPrefix()
+     */
     public function testGetPrefixPerson(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -540,6 +600,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals('person/1', $prefix);
     }
 
+    /**
+     * @see LocalStorage::getPrefix()
+     */
     public function testGetPrefixTemp(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -554,6 +617,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals('temp/organization/1', $prefix);
     }
 
+    /**
+     * @see LocalStorage::getPrefix()
+     */
     public function testGetPrefixWithType(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])
@@ -568,6 +634,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals('organization/1/licence_cmf', $prefix);
     }
 
+    /**
+     * @see LocalStorage::guessMimeTypeFromFilename()
+     */
     public function testGuessMimeTypeFromFilename(): void {
         $this->assertEquals('application/pdf', TestableLocalStorage::guessMimeTypeFromFilename('file.pdf'));
         $this->assertEquals('text/csv', TestableLocalStorage::guessMimeTypeFromFilename('file.csv'));
@@ -579,6 +648,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals(null, TestableLocalStorage::guessMimeTypeFromFilename('file.invalid'));
     }
 
+    /**
+     * @see LocalStorage::getMimeTypeFromExt()
+     */
     public function testGuessMimeTypeFromExt(): void {
         $this->assertEquals('application/pdf', TestableLocalStorage::getMimeTypeFromExt('pdf'));
         $this->assertEquals('text/csv', TestableLocalStorage::getMimeTypeFromExt('csv'));
@@ -591,6 +663,9 @@ class LocalStorageTest extends TestCase
         $this->assertEquals(null, TestableLocalStorage::getMimeTypeFromExt('invalid'));
     }
 
+    /**
+     * @see LocalStorage::getOrganizationAndPersonFromOwner()
+     */
     public function testGetOrganizationAndPersonFromOwner(): void {
         $fileStorage = $this->getMockBuilder(TestableLocalStorage::class)
             ->setConstructorArgs([$this->filesystemMap, $this->entityManager, $this->accessRepository, $this->iriConverter])

+ 52 - 2
tests/Service/Typo3/Typo3ServiceTest.php

@@ -1,6 +1,6 @@
 <?php /** @noinspection PhpUnhandledExceptionInspection */
 
-namespace App\Test\Service\Typo3;
+namespace App\Tests\Service\Typo3;
 
 use App\Service\Typo3\Typo3Service;
 use PHPUnit\Framework\TestCase;
@@ -19,6 +19,9 @@ class Typo3ServiceTest extends TestCase
         $this->typo3Client = $this->getMockBuilder(HttpClientInterface::class)->disableOriginalConstructor()->getMock();
     }
 
+    /**
+     * @see Typo3Service::sendCommand()
+     */
     public function testSendCommand(): void
     {
         $response = $this->getMockBuilder(ResponseInterface::class)->disableOriginalConstructor()->getMock();
@@ -37,6 +40,29 @@ class Typo3ServiceTest extends TestCase
         $typo3Service->sendCommand('foo', ['param' => 'bar']);
     }
 
+    /**
+     * @see Typo3Service::clearSiteCache()
+     */
+    public function testClearSiteCache(): void
+    {
+        $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)
+            ->setMethodsExcept(['clearSiteCache'])
+            ->disableOriginalConstructor()
+            ->getMock();
+
+
+        $response = $this->getMockBuilder(ResponseInterface::class)->disableOriginalConstructor()->getMock();
+        $typo3Service->expects(self::once())
+            ->method('sendCommand')->
+            with('/otadmin/site/clear-cache', ['organization-id' => 1])
+            ->willReturn($response);
+
+        $typo3Service->clearSiteCache(1);
+    }
+
+    /**
+     * @see Typo3Service::createSite()
+     */
     public function testCreateSite(): void
     {
         $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)
@@ -53,7 +79,10 @@ class Typo3ServiceTest extends TestCase
         $typo3Service->createSite(1);
     }
 
-    public function testClearSiteCache(): void
+    /**
+     * @see Typo3Service::updateSite()
+     */
+    public function testUpdateSite(): void
     {
         $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)
             ->setMethodsExcept(['updateSite'])
@@ -70,6 +99,9 @@ class Typo3ServiceTest extends TestCase
         $typo3Service->updateSite(1);
     }
 
+    /**
+     * @see Typo3Service::deleteSite()
+     */
     public function testDeleteSite(): void
     {
         $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)
@@ -87,6 +119,9 @@ class Typo3ServiceTest extends TestCase
         $typo3Service->deleteSite(1);
     }
 
+    /**
+     * @see Typo3Service::undeleteSite()
+     */
     public function testUndeleteSite(): void
     {
         $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)
@@ -104,6 +139,9 @@ class Typo3ServiceTest extends TestCase
         $typo3Service->undeleteSite(1);
     }
 
+    /**
+     * @see Typo3Service::setSiteDomain()
+     */
     public function testSetSiteDomain(): void
     {
         $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)
@@ -121,6 +159,9 @@ class Typo3ServiceTest extends TestCase
         $typo3Service->setSiteDomain(1, 'new-domain', false);
     }
 
+    /**
+     * @see Typo3Service::setSiteDomain()
+     */
     public function testSetSiteDomainWithRedirection(): void
     {
         $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)
@@ -138,6 +179,9 @@ class Typo3ServiceTest extends TestCase
         $typo3Service->setSiteDomain(1, 'new-domain', true);
     }
 
+    /**
+     * @see Typo3Service::resetSitePerms()
+     */
     public function testResetSitePerms(): void
     {
         $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)
@@ -155,6 +199,9 @@ class Typo3ServiceTest extends TestCase
         $typo3Service->resetSitePerms(1);
     }
 
+    /**
+     * @see Typo3Service::getSiteStatus()
+     */
     public function testGetSiteStatus(): void
     {
         $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)
@@ -172,6 +219,9 @@ class Typo3ServiceTest extends TestCase
         $typo3Service->getSiteStatus(1);
     }
 
+    /**
+     * @see Typo3Service::addRedirection()
+     */
     public function testAddRedirection(): void
     {
         $typo3Service = $this->getMockBuilder(TestableTypo3Service::class)

+ 3 - 0
tests/Service/Utils/ArrayUtilsTest.php

@@ -7,6 +7,9 @@ use PHPUnit\Framework\TestCase;
 
 class ArrayUtilsTest extends TestCase
 {
+    /**
+     * @øee ArrayUtils::getChanges()
+     */
     public function testGetChanges(): void
     {
         $arrayUtils = new ArrayUtils();

+ 32 - 2
tests/Service/Utils/DatesUtilsTest.php

@@ -1,7 +1,6 @@
 <?php
 namespace App\Tests\Service\Utils;
 
-use App\Service\Utils\Dates;
 use App\Service\Utils\DatesUtils;
 use PHPUnit\Framework\TestCase;
 
@@ -9,6 +8,7 @@ class DatesUtilsTest extends TestCase
 {
     /**
      * @see DatesUtils::isIntervalIsValidNow()
+     * @noinspection PhpRedundantOptionalArgumentInspection
      */
     public function testIsIntervalIsValidNow():void
     {
@@ -23,4 +23,34 @@ class DatesUtilsTest extends TestCase
     {
         $this->assertFalse(DatesUtils::isIntervalIsValidNow(new \DateTime('2019-01-02'), new \DateTime('2020-01-02')));
     }
-}
+
+    /**
+     * @see DatesUtils::new()
+     */
+    public function testNew(): void {
+        DatesUtils::clearFakeDatetime();
+
+        self::assertEquals('2021-01-01', DatesUtils::new('2021-01-01')->format('Y-m-d'));
+
+        $now = new \DateTime();
+
+        self::assertEquals(
+            $now->format('Y-m-d'),
+            DatesUtils::new()->format('Y-m-d')
+        );
+
+        DatesUtils::setFakeDatetime('2021-01-01');
+
+        /** @noinspection PhpRedundantOptionalArgumentInspection */
+        self::assertEquals('2021-01-01', DatesUtils::new('now')->format('Y-m-d'));
+
+        DatesUtils::clearFakeDatetime();
+
+        $now = new \DateTime();
+
+        self::assertEquals(
+            $now->format('Y-m-d'),
+            DatesUtils::new()->format('Y-m-d')
+        );
+    }
+}

+ 80 - 0
tests/Service/Utils/EntityUtilsTest.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace App\Tests\Service\Utils;
+
+use App\Entity\Access\Access;
+use App\Entity\Billing\BillingSetting;
+use App\Entity\Billing\ResidenceArea;
+use App\Entity\Education\EducationTiming;
+use App\Entity\Organization\Organization;
+use App\Service\Utils\EntityUtils;
+use PHPUnit\Framework\TestCase;
+
+class TestableEntityUtils extends EntityUtils {
+    public function organizationDefaultValue($entity, Access $access): void { parent::organizationDefaultValue($entity, $access); }
+    public function billingSettingDefaultValueDefaultValue($entity, Access $access): void { parent::billingSettingDefaultValueDefaultValue($entity, $access); }
+}
+
+class EntityUtilsTest extends TestCase
+{
+    /**
+     * @see EntityUtils::defaultValueSettersByAccess()
+     */
+    public function testDefaultValueSettersByAccess(): void {
+        $utils = $this->getMockBuilder(TestableEntityUtils::class)
+            ->setMethodsExcept(['defaultValueSettersByAccess'])
+            ->getMock();
+
+        $entity = $this->getMockBuilder(Organization::class)->getMock();
+        $access = $this->getMockBuilder(Access::class)->getMock();
+
+        $utils->expects(self::once())->method('organizationDefaultValue')->with($entity, $access);
+        $utils->expects(self::once())->method('billingSettingDefaultValueDefaultValue')->with($entity, $access);
+
+        $utils->defaultValueSettersByAccess($entity, $access);
+    }
+
+    /**
+     * @see EntityUtils::organizationDefaultValue()
+     */
+    public function testOrganizationDefaultValue(): void {
+        $utils = $this->getMockBuilder(TestableEntityUtils::class)
+            ->setMethodsExcept(['organizationDefaultValue'])
+            ->getMock();
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+
+        $entity = new EducationTiming();  // Can't test this method with a mocked entity...
+
+        $access = $this->getMockBuilder(Access::class)->getMock();
+        $access->method('getOrganization')->willReturn($organization);
+
+        $utils->organizationDefaultValue($entity, $access);
+
+        $this->assertEquals($organization, $entity->getOrganization());
+    }
+
+    /**
+     * @see EntityUtils::billingSettingDefaultValueDefaultValue()
+     */
+    public function testBillingSettingDefaultValueDefaultValue(): void {
+        $utils = $this->getMockBuilder(TestableEntityUtils::class)
+            ->setMethodsExcept(['billingSettingDefaultValueDefaultValue'])
+            ->getMock();
+
+        $entity = new ResidenceArea();  // Can't test this method with a mocked entity...
+
+        $billingSetting = $this->getMockBuilder(BillingSetting::class)->getMock();
+
+        $organization = $this->getMockBuilder(Organization::class)->getMock();
+        $organization->method('getBillingSetting')->willReturn($billingSetting);
+
+        $access = $this->getMockBuilder(Access::class)->getMock();
+        $access->method('getOrganization')->willReturn($organization);
+
+        $utils->billingSettingDefaultValueDefaultValue($entity, $access);
+
+        $this->assertEquals($billingSetting, $entity->getBillingSetting());
+    }
+
+}

+ 0 - 8
tests/Service/Utils/GpsCoordinateUtilsTest.php

@@ -1,11 +1,8 @@
 <?php
 namespace App\Tests\Service\Utils;
 
-use App\ApiResources\Utils\GpsCoordinate;
 use App\Service\Utils\GpsCoordinateUtils;
 use PHPUnit\Framework\TestCase;
-use Symfony\Component\HttpClient\MockHttpClient;
-use Symfony\Component\HttpClient\Response\MockResponse;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Symfony\Contracts\HttpClient\HttpClientInterface;
 use Symfony\Contracts\HttpClient\ResponseInterface;
@@ -13,15 +10,10 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
 class GpsCoordinateUtilsTest extends TestCase
 {
     private HttpClientInterface $client;
-    private string $response;
-    private string $responseReverse;
 
     public function setUp(): void
     {
         $this->client = $this->getMockBuilder(HttpClientInterface::class)->disableOriginalConstructor()->getMock();
-
-        $this->response = '[{"place_id":124047700,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright","osm_type":"way","osm_id":146744176,"boundingbox":["46.040309","46.0413942","6.584711","6.5864561"],"lat":"46.0405718","lon":"6.5857964","display_name":"Chemin des Rirets, La Frasse, Romme, Nancy-sur-Cluses, Bonneville, Haute-Savoie, Auvergne-Rhône-Alpes, France métropolitaine, 74300, France","place_rank":26,"category":"highway","type":"residential","importance":0.8099999999999999}]';
-        $this->responseReverse = '{"place_id":124302276,"licence":"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright","osm_type":"way","osm_id":146744171,"lat":"46.04082792421903","lon":"6.585562827387761","place_rank":26,"category":"highway","type":"unclassified","importance":0.09999999999999998,"addresstype":"road","name":"Chemin des Larrets","display_name":"Chemin des Larrets, La Frasse, Romme, Nancy-sur-Cluses, Bonneville, Haute-Savoie, Auvergne-Rhône-Alpes, France métropolitaine, 74300, France","address":{"road":"Chemin des Larrets","hamlet":"La Frasse","village":"Romme","municipality":"Bonneville","county":"Haute-Savoie","state":"Auvergne-Rhône-Alpes","country":"France","postcode":"74300","country_code":"fr"},"boundingbox":["46.0404896","46.040881","6.585013","6.5874704"]}';
     }
 
     /**

+ 2 - 2
tests/Service/Utils/ParserTest.php

@@ -6,7 +6,7 @@ use PHPUnit\Framework\TestCase;
 
 class ParserTest extends TestCase
 {
-    const FIXTURES = __DIR__.'/fixtures';
+    public const FIXTURES = __DIR__.'/fixtures';
 
     /**
      * @see Parser::yamlParser()
@@ -19,4 +19,4 @@ class ParserTest extends TestCase
         $this->assertIsArray($parsingArray['opentalent']);
         $this->assertEquals('ROLE_COTISATION', $parsingArray['opentalent']['modulesbyconditions']['CotisationCall']['roles'][0]);
     }
-}
+}

+ 109 - 0
tests/Service/Utils/ReflectionTest.php

@@ -0,0 +1,109 @@
+<?php
+
+namespace App\Tests\Service\Utils;
+
+use App\Service\Utils\Reflection;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class ReflectionTestSubject {
+    public function __construct($c) {}
+    public function multiply(int $a, int $b): int { return $a * $b; }
+    public static function add(int $a, int $b): int { return $a + $b; }
+}
+
+class ReflectionTest extends TestCase
+{
+    private ContainerInterface | MockObject $container;
+
+    public function setUp(): void {
+        $this->container = $this->getMockBuilder(ContainerInterface::class)->disableOriginalConstructor()->getMock();
+    }
+
+    /**
+     * @see Reflection::dynamicInvokeServiceWithArgsAndMethod()
+     */
+    public function testDynamicInvokeServiceWithArgsAndMethod(): void {
+        $reflection = $this->getMockBuilder(Reflection::class)
+            ->setConstructorArgs([$this->container])
+            ->setMethodsExcept(['dynamicInvokeServiceWithArgsAndMethod'])
+            ->getMock();
+
+        $subject = new ReflectionTestSubject(1);
+
+        $this->container->method('get')->with('subject')->willReturn($subject);
+
+        $this->assertEquals(
+            6,
+            $reflection->dynamicInvokeServiceWithArgsAndMethod('subject', 'multiply', [2, 3])
+        );
+    }
+
+    /**
+     * @see Reflection::dynamicInvokeServiceWithArgsAndMethod()
+     */
+    public function testDynamicInvokeServiceWithArgsAndClassNotFound(): void {
+        $reflection = $this->getMockBuilder(Reflection::class)
+            ->setConstructorArgs([$this->container])
+            ->setMethodsExcept(['dynamicInvokeServiceWithArgsAndMethod'])
+            ->getMock();
+
+        $this->container->method('get')->with('subject')->willReturn(null);
+
+        $this->expectException(\LogicException::class);
+
+        $reflection->dynamicInvokeServiceWithArgsAndMethod('subject', 'invalid');
+    }
+
+    /**
+     * @see Reflection::dynamicInvokeServiceWithArgsAndMethod()
+     */
+    public function testDynamicInvokeServiceWithArgsAndMethodInexistant(): void {
+        $reflection = $this->getMockBuilder(Reflection::class)
+            ->setConstructorArgs([$this->container])
+            ->setMethodsExcept(['dynamicInvokeServiceWithArgsAndMethod'])
+            ->getMock();
+
+        $this->container->method('get')->with('subject')->willReturn(ReflectionTestSubject::class);
+
+        $this->expectException(\LogicException::class);
+
+        $reflection->dynamicInvokeServiceWithArgsAndMethod('subject', 'invalid');
+    }
+
+    /**
+     * @see Reflection::dynamicInvokeClassWithArgsAndMethod()
+     */
+    public function testDynamicInvokeClassWithArgsAndMethod(): void {
+        $reflection = $this->getMockBuilder(Reflection::class)
+            ->setConstructorArgs([$this->container])
+            ->setMethodsExcept(['dynamicInvokeClassWithArgsAndMethod'])
+            ->getMock();
+
+        $this->assertEquals(
+            6,
+            $reflection->dynamicInvokeClassWithArgsAndMethod(
+                ReflectionTestSubject::class,
+                'multiply',
+                [2, 3],
+                [1]
+            )
+        );
+    }
+
+    /**
+     * @see Reflection::dynamicInvokeClassWithArgsAndMethod()
+     */
+    public function testDynamicInvokeClassWithArgsAndMethodWithStatic(): void {
+        $reflection = $this->getMockBuilder(Reflection::class)
+            ->setConstructorArgs([$this->container])
+            ->setMethodsExcept(['dynamicInvokeClassWithArgsAndMethod'])
+            ->getMock();
+
+        $this->assertEquals(
+            5,
+            $reflection->dynamicInvokeClassWithArgsAndMethod(ReflectionTestSubject::class, 'add', [2, 3])
+        );
+    }
+}

+ 0 - 2
tests/Service/Utils/SiretTest.php

@@ -4,8 +4,6 @@ namespace App\Tests\Service\Utils;
 
 use App\Service\Utils\Siret;
 use PHPUnit\Framework\TestCase;
-use Symfony\Component\HttpClient\MockHttpClient;
-use Symfony\Component\HttpClient\Response\MockResponse;
 use Symfony\Contracts\HttpClient\HttpClientInterface;
 use Symfony\Contracts\HttpClient\ResponseInterface;
 

+ 21 - 5
tests/Service/Utils/UrlBuilderTest.php

@@ -1,4 +1,4 @@
-<?php
+<?php /** @noinspection HttpUrlsUsage */
 
 namespace App\Tests\Service\Utils;
 
@@ -7,7 +7,11 @@ use PHPUnit\Framework\TestCase;
 
 class UrlBuilderTest extends TestCase
 {
-    public function testConcatPath() {
+    /**
+     * @see UrlBuilder::concatPath()
+     */
+    public function testConcatPath(): void
+    {
         $this->assertEquals(
             'https://domain.org/abc/def',
             UrlBuilder::concatPath('https://domain.org', 'abc/def')
@@ -22,7 +26,11 @@ class UrlBuilderTest extends TestCase
         );
     }
 
-    public function testConcatParameters() {
+    /**
+     * @see UrlBuilder::concatParameters()
+     */
+    public function testConcatParameters(): void
+    {
         $this->assertEquals(
             'https://domain.org/abc',
             UrlBuilder::concatParameters('https://domain.org/abc', [])
@@ -37,7 +45,11 @@ class UrlBuilderTest extends TestCase
         );
     }
 
-    public function testPrependHttps() {
+    /**
+     * @see UrlBuilder::prependHttps()
+     */
+    public function testPrependHttps(): void
+    {
         $this->assertEquals(
             'https://domain.org/abc',
             UrlBuilder::prependHttps('https://domain.org/abc')
@@ -52,7 +64,11 @@ class UrlBuilderTest extends TestCase
         );
     }
 
-    public function testConcat() {
+    /**
+     * @see UrlBuilder::concat()
+     */
+    public function testConcat(): void
+    {
         $this->assertEquals(
             'https://domain.org/abc?a=1',
             UrlBuilder::concat('domain.org', 'abc', ['a' => 1], true)

+ 3 - 0
tests/Service/Utils/UuidTest.php

@@ -7,6 +7,9 @@ use PHPUnit\Framework\TestCase;
 
 class UuidTest extends TestCase
 {
+    /**
+     * @see Uuid::uuid()
+     */
     public function testUuid(): void
     {
         $this->assertMatchesRegularExpression('/\w{8}/', Uuid::uuid());